listen()函數僅在TCP服務器端調用,它做兩個事情:將套接字轉換到LISTEN狀態和設置套接上的最大連接隊列。listen()對應的內核實現為sys_listen(),下面開始對其實現作具體的分析。
一、sys_listen()函數
sys_listen()的源碼實現及分析如下所示:
/*
* Perform a listen. Basically, we allow the protocol to do anything
* necessary for a listen, and if that works, we mark the socket as
* ready for listening.
*/
SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{
struct socket *sock;
int err, fput_needed;
int somaxconn;
sock = sockfd_lookup_light(fd, &err, &fput_needed);
if (sock) {
/*
* sysctl_somaxconn存儲的是服務器監聽時,允許每個套接字連接隊列長度
* 的最大值,默認值是SOMAXCONN,即128,在sysctl_core_net_init()函數中初始化。
* 在proc文件系統中可以通過修改/proc/sys/net/core/somaxconn文件來修改這個值。
*/
somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
/*
* 如果指定的最大連接數超過系統限制,則使用系統當前允許的連接隊列
* 中連接的最大數。
*/
if ((unsigned)backlog > somaxconn)
backlog = somaxconn;
err = security_socket_listen(sock, backlog);
if (!err)
/*
* 如果是TCP套接字,sock->ops指向的是inet_stream_ops,
* sock->ops是在inet_create()函數中初始化,所以listen接口
* 調用的是inet_listen()函數。
*/
err = sock->ops->listen(sock, backlog);
fput_light(sock->file, fput_needed);
}
return err;
}
sys_listen()的代碼流程圖如下所示:
sys_listen()的代碼流程和sys_bind()很像,都是先調用sockfd_lookup_light()獲取描述符對應的socket實例,然後通過調用sock->ops中的操作接口來完成真正的操作。接下來看這段代碼:
if ((unsigned)backlog > somaxconn)
backlog = somaxconn;
這裡可以看出,如果指定的最大連接隊列數超過系統限制,會使用系統中設置的最大連接隊列數。所以,如果想擴大套接字的連接隊列,只調整listen()的backlog參數是沒用的,還要修改系統的設置才行。