首页 / 操作系统 / Linux / Linux内核跟踪sys_listen函数笔记
/* * AUTHOR: anhk * DATE: 2007-5-24 * KERNEL: 2.6.20 * * 这是学习网络部分的笔记, 比较粗糙, 并且把一些引用计数代码去掉了. * 还有部分加锁的代码 * sys_socketcall[net/socket.c]函数是整个网络的中断入口函数 */ 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; }
收藏该网址