对于面向连接的socket,需要会用监听连接的到来,并且使用backlog参数来限制连接数量;具体backlog限制的内容,请参考本博另外一篇文章,本文结尾提供链接地址;
1 /* 2 * Perform a listen. Basically, we allow the protocol to do anything 3 * necessary for a listen, and if that works, we mark the socket as 4 * ready for listening. 5 */ 6 7 SYSCALL_DEFINE2(listen, int, fd, int, backlog) 8 { 9 struct socket *sock; 10 int err, fput_needed; 11 int somaxconn; 12 13 /* 查找socket结构 */ 14 sock = sockfd_lookup_light(fd, &err, &fput_needed); 15 if (sock) { 16 somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn; 17 18 /* backlog不能超过设置值 */ 19 if ((unsigned int)backlog > somaxconn) 20 backlog = somaxconn; 21 22 /* 安全模块检查 */ 23 err = security_socket_listen(sock, backlog); 24 if (!err) 25 /* 调用对应类型的listen函数,只有SOCK_STREAM支持listen */ 26 err = sock->ops->listen(sock, backlog); 27 28 fput_light(sock->file, fput_needed); 29 } 30 return err; 31 }
inet_listen、数对socket类型,连接状态等进行基本的校验,如果已经处于listen状态,则只会调整backlog的值,如果尚未处于listen状态,则调用传输层的listen实现函数进行针对的listen操作;
1 /* 2 * Move a socket into listening state. 3 */ 4 int inet_listen(struct socket *sock, int backlog) 5 { 6 struct sock *sk = sock->sk; 7 unsigned char old_state; 8 int err; 9 10 lock_sock(sk); 11 12 err = -EINVAL; 13 14 /* 检查socket状态和类型,仅支持SOCK_STREAM */ 15 if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM) 16 goto out; 17 18 /* 记录连接当前状态 */ 19 old_state = sk->sk_state; 20 21 /* 检查连接状态,需为close或者listen */ 22 if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN))) 23 goto out; 24 25 /* Really, if the socket is already in listen state 26 * we can only allow the backlog to be adjusted. 27 */ 28 /* 如果尚未listen过 */ 29 if (old_state != TCP_LISTEN) { 30 /* Enable TFO w/o requiring TCP_FASTOPEN socket option. 31 * Note that only TCP sockets (SOCK_STREAM) will reach here. 32 * Also fastopen backlog may already been set via the option 33 * because the socket was in TCP_LISTEN state previously but 34 * was shutdown() rather than close(). 35 */ 36 /* TFO相关*/ 37 if ((sysctl_tcp_fastopen & TFO_SERVER_WO_SOCKOPT1) && 38 (sysctl_tcp_fastopen & TFO_SERVER_ENABLE) && 39 !inet_csk(sk)->icsk_accept_queue.fastopenq.max_qlen) { 40 fastopen_queue_tune(sk, backlog); 41 tcp_fastopen_init_key_once(true); 42 } 43 44 /* 开始监听 */ 45 err = inet_csk_listen_start(sk, backlog); 46 if (err) 47 goto out; 48 } 49 50 /* 设置backlog,包括已经listen过,调整backlog */ 51 sk->sk_max_ack_backlog = backlog; 52 err = 0; 53 54 out: 55 release_sock(sk); 56 return err; 57 } 58 EXPORT_SYMBOL(inet_listen);
其中tcp层对于listen的实现,实际上也是调用了inet_csk_get_port函数,与bind()系统调用在tcp层的调用函数一致,listen的时候,如果发现尚未绑定端口,则自动选择端口绑定,已经绑定端口,则进行相关检查;具体请参考bind()系统调用的tcp层实现;请阅读<TCP层bind系统调用的实现分析>;
关于TCP的listen函数和backlog参数说明,请移步以下文章<TCP之listen&backlog>;