Linux内核跟踪函数sys_listen的学习
时间:2011-04-30 来源:Java爱
asmlinkage long sys_socketcall(int call, unsigned long __user *args);
{
unsigned long a[6];
/* 从用户空间得到信息,该函数是SMP安全的 */
if (copy_from_user(a, args, nargs[call]))
return -EFAULT;
switch (call) {
case SYS_SOCKET:
err = sys_socket(a[0], a[1], a[2]);
break;
case SYS_BIND:
err = sys_bind(a[0], (struct sockaddr __user *)a[1], a[2]);
break;
case SYS_CONNECT:
err = sys_connect(a[0], (struct sockaddr __user *)a[1], a[2]);
break;
....
}
}
/*
* 下面跟踪sys_listen函数
*
* 该函数首先通过调用sockfd_lookup_light函数,
* 根据描述符获得拥有该描述符的struct file *file结构, 然后根据该file结构获得struct socket *sock结构
* 从用户空间得到fd和backlog
* 调用sock->ops->listen()函数, 该函数在tcp中是inet_listen[net/ipv4/af_inet.c]
* 如果是udp, 则sock->ops->listen = sock_no_listen,直接返回
*/
asmlinkage long sys_listen(int fd, int backlog)
{
struct socket *sock;
int err, fput_needed;
/*
* 查找到具有该描述符的struct socket *sock结构
*/
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
if ((unsigned)backlog > sysctl_somaxconn)
backlog = sysctl_somaxconn;
/*
* sock->ops = &inet_stream_ops || &inet_dgram_ops,
*
* TCP: sock->ops->listen = inet_listen
* UDP: sock->ops->listen = sock_no_listen
*/
err = sock->ops->listen(sock, backlog);
fput_light(sock->file, fput_needed);
}
return err;
}
/*
* inet_listen函数中,首先判断该sock的状态是否SS_UNCONNECTED 且类型 是否是SOCK_STREAM, 否的话直接退出
* 然后 判断该sock->sk是否是已侦听状态, 如果是就直接设置backlog, 退出
* 否则调用 inet_csk_listen_start[net/ipv4/inet_connection_sock.c]
*/
int inet_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
unsigned char old_state;
int err;
lock_sock(sk);
err = -EINVAL;
if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
goto out;
old_state = sk->sk_state;
if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN)))
goto out;
/* Really, if the socket is already in listen state
* we can only allow the backlog to be adjusted.
*/
if (old_state != TCP_LISTEN) {
err = inet_csk_listen_start(sk, backlog);
if (err)
goto out;
}
sk->sk_max_ack_backlog = backlog;
err = 0;
out:
release_sock(sk);
return err;
}
/*
* inet_csk_listen_start[net/ipv4/inet_connection_sock.c]
* 该函数首先调用reqsk_queue_alloc[net/core/request_sock.c]申请一块内存, 存放侦听 队列 for accept;
* 然后设置侦听状态,并把该struct sock *sk放入tcp_hashinfo.ehash中
*/
int inet_csk_listen_start(struct sock *sk, const int nr_table_entries)
{
struct inet_sock *inet = inet_sk(sk);
struct inet_connection_sock *icsk = inet_csk(sk);
int rc = reqsk_queue_alloc(&icsk->icsk_accept_queue, nr_table_entries);
if (rc != 0)
return rc;
sk->sk_max_ack_backlog = 0;
sk->sk_ack_backlog = 0;
/*
* only memset
* memset(&inet_csk(sk)->icsk_ack, 0, sizeof(inet_csk(sk)->icsk_ack));
*/
inet_csk_delack_init(sk);
/*
* 设置侦听状态, 并且判断该端口没有被使用
* 然后 加入到tcp_hashinfo.ehash中
* return 0;
*/
sk->sk_state = TCP_LISTEN;
if (!sk->sk_prot->get_port(sk, inet->num)) {
inet->sport = htons(inet->num);
sk_dst_reset(sk);
sk->sk_prot->hash(sk);
return 0;
}
sk->sk_state = TCP_CLOSE;
__reqsk_queue_destroy(&icsk->icsk_accept_queue);
return -EADDRINUSE;
}
Linux好学堂, www.linuxhao.com,Linux应用开发综合性技术网站,提供Linux视频教程,Linux培训教程,Linux技术资料免费下载。