一、Linux 4.19内核listen系统调用代码注释
/** 开始对一个 socket 进行监听。这个函数做一些准备工作以便 socket 可以开始监听,* 如果操作成功,则将 socket 标记为准备好监听的状态。*/int __sys_listen(int fd, int backlog)
{struct socket *sock; // 定义 socket 结构指针int err, fput_needed; // 错误码变量和引用计数释放标志int somaxconn; // 用于存储系统限制的最大监听队列长度// 试图通过文件描述符 fd 查找对应的 socket 结构sock = sockfd_lookup_light(fd, &err, &fput_needed);if (sock) { // 如果找到了对应的 socket// 获取系统配置的最大监听队列长度(SOMAXCONN 的值)somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;// 如果请求的 backlog 超过了系统设置的最大值,则强制降低至系统设置的最大值if ((unsigned int)backlog > somaxconn)backlog = somaxconn;// 调用安全模块相关函数,对 socket 进行监听之前的安全检查err = security_socket_listen(sock, backlog);// 安全检查通过,然后实际通过 socket 操作来进行监听if (!err)err = sock->ops->listen(sock, backlog);// 对 sock 文件描述符的引用计数减一,如果需要的话释放它fput_light(sock->file, fput_needed);}// 返回操作的结果,如果出现错误 err 中会有错误码return err;
}// syscall 宏定义,它会将 listen 这个系统调用附加到内核中的系统调用表,使得用户程序可以通过系统调用接口使用
SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{// 直接调用上面定义的 __sys_listen 函数来处理系统调用return __sys_listen(fd, backlog);
}
二、代码解释
这段代码是 Linux 内核中负责设置 socket 监听状态的 C 函数。以下是代码的解释:
首先定义了一个内核函数 __sys_listen,这个函数是设置 socket 进入监听状态的实际实现。它接受两个参数:`fd` 和 backlog。`fd` 是文件描述符,代表一个打开的 socket;`backlog` 是一个整数,它制定了 socket 可以排队的最大连接数。
函数定义中的变量解释:
- struct socket *sock;: 定义了一个 socket 结构体指针 sock,用于储存查找到的 socket 信息。
- int err, fput_needed;: err 用于存放错误代码,`fput_needed` 用于标记文件描述符引用计数的递减是否需要。
- int somaxconn;: 用于存储系统定义的最大监听队列长度。
函数执行过程分解:
1. sock = sockfd_lookup_light(fd, &err, &fput_needed);: 通过文件描述符 fd 来查找对应的 socket。如果找不到,则 sock 为 NULL,并且 err 会被设置为相应的错误码。
2. if (sock) { ... }: 如果找到了 socket,就进入大括号中执行相关操作。
3. somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;: 获取系统配置的最大监听队列长度(SOMAXCONN 值),进行接下来的队列长度限制。
4. if ((unsigned int)backlog > somaxconn) backlog = somaxconn;: 如果传入的 backlog 值大于 somaxconn,则将其限制为 somaxconn。
5. err = security_socket_listen(sock, backlog);: 调用安全模块相关函数进行监听之前的安全检查。
6. if (!err) err = sock->ops->listen(sock, backlog);: 如果安全检查没有产生错误,则调用 socket 的监听操作。如果监听操作失败,则将错误码存入 err。
7. fput_light(sock->file, fput_needed);: 对应文件描述符的引用计数进行递减,若需要则释放文件。
最后,`__sys_listen` 函数返回 err,这个返回值表示了监听操作的结果。
接下来,使用 SYSCALL_DEFINE2(listen, int, fd, int, backlog) 宏定义 syscall 接口 listen,这个接口让用户空间的程序可以调用内核中定义的 __sys_listen 函数进行 socket 监听设置。这个宏基本上是定义了一个符合 Linux syscall 调用约定的包装函数,可以被 syscall 表访问。