从内核角度分析 listen() 系统调用的 backlog 参数作用

简介: 从内核角度分析 listen() 系统调用的 backlog 参数作用

编写服务端程序时,需要调用 listen() 系统调用来开始监听请求连接,listen() 系统调用的原型如下:

int listen(int sockfd, int backlog);

可以看到,listen() 系统调用需要传入两个参数,第一个 sockfd 表示监听的 socket 句柄,而 backlog 参数表示接收请求队列的长度。对于第一个参数比较容易理解,那么第二个参数的作用是什么呢?下面我们来分析一下。

listen() 系统调用在内核的实现

当我们在程序中调用 listen() 系统调用时,会触发调用内核的 sys_listen() 函数,sys_listen() 函数的实现如下:

asmlinkage long sys_listen(int fd, int backlog){
    struct socket *sock;
    int err;
    if ((sock = sockfd_lookup(fd, &err)) != NULL) { /* 步骤1 */
            if ((unsigned) backlog > SOMAXCONN)
                        backlog = SOMAXCONN;
            err=sock->ops->listen(sock, backlog);       /* 步骤2 */
            sockfd_put(sock);
    }    
    return err;
}

sys_listen() 函数的实现比较简单,过程如下:

  • 步骤1:首先调用 sockfd_lookup() 函数查找文件句柄 fd 对应的 socket 对象。
  • 步骤2:通过调用 socket 对象的的 listen() 方法来进行监听操作。

对于 TCP协议 来说,socket 对象的 listen() 方法会绑定到 inet_listen() 函数。所以 步骤2 最后会调用 inet_listen() 函数,inet_listen() 函数的实现如下:

int inet_listen(struct socket *sock, int backlog)
{
    struct sock *sk = sock->sk;
    unsigned char old_state;
    int err;
    lock_sock(sk);
    ...
    old_state = sk->state;
    if (!((1<<old\_state)&(TCPF\_CLOSE|TCPF_LISTEN)))
        goto out;
    if (old\_state != TCP\_LISTEN) {
        err = tcp\_listen\_start(sk);   /* 步骤1 */
        if (err)
            goto out;
    }
    sk->max\_ack\_backlog = backlog;    /* 步骤2 */
    err = 0;
out:
    release_sock(sk);
    return err;
}

inet_listen() 函数的实现也非常简单,主要分为两个步骤:

  • 步骤1:调用 tcp_listen_start() 函数把 socket 对象的状态设置为 TCP_LISTEN
  • 步骤2:把 socket 对象的 sk 成员变量的 max_ack_backlog 字段设置为 backlog

其中 max_ack_backlog 字段就是用于保存最大接收连接队列的长度,至此 listen() 函数的工作就完成了,那么内核在哪里限制接收连接队列的呢?

内核限制TCP连接队列

当网卡接收到数据时,会接收到数据包并封装成 sk_buff 对象,如果接收到的数据包是一个 TCP协议 的数据包,那么内核将会把数据包提交给 tcp_v4_rcv() 函数处理。我们只关注限制TCP连接队列的实现,所以这里直接给出限制逻辑相关的调用链:

tcp\_v4\_rcv() 
    \\__> tcp\_v4\_do_rcv() 
             \\__> tcp\_v4\_hnd_req() 
                        \\__> tcp\_check\_req() 
                                   \\__> tcp\_v4\_syn\_recv\_sock()

从上面的调用链可以看出,最后调用的函数是 tcp_v4_syn_recv_sock()tcp_v4_syn_recv_sock() 函数的作用是当对端连接完成 TCP三次握手 后,将创建一个新的 socket 连接对象。我们来看看 tcp_v4_syn_recv_sock() 函数对连接队列的限制逻辑:

tcp\_v4\_syn\_recv\_sock(struct sock *sk, 
                     struct sk_buff *skb,
                     struct open_request *req,
                     struct dst_entry *dst)
{
    struct tcp_opt *newtp;
    struct sock *newsk;
    if (tcp\_acceptq\_is_full(sk)) /* 判断接收队列是否超过限制 */
        goto exit_overflow;
    ...
    return newsk;
exit_overflow:
    NET\_INC\_STATS_BH(ListenOverflows);
exit:
    NET\_INC\_STATS_BH(ListenDrops);
    dst_release(dst);
    return NULL;
}

tcp_v4_syn_recv_sock() 函数首先调用了 tcp_acceptq_is_full() 来判断接收队列是否已经超过限制,如果超过限制就不再创建新的连接,tcp_acceptq_is_full() 函数的实现如下:

static inline int tcp\_acceptq\_is_full(struct sock *sk) {
    return sk->ack\_backlog > sk->max\_ack_backlog;
}

tcp_acceptq_is_full() 函数很简单,就是判断当前接收队列的数量是否超过了限制的最大数量,如果是就返回true,至此我们对 backlog 参数的作用分析完毕。

原文:https://mp.weixin.qq.com/s/DfJvIoLpaU5G1H-462qjRQ

相关实践学习
深入解析Docker容器化技术
Docker是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的Linux机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。Docker是世界领先的软件容器平台。开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。运维人员利用Docker可以在隔离容器中并行运行和管理应用,获得更好的计算密度。企业利用Docker可以构建敏捷的软件交付管道,以更快的速度、更高的安全性和可靠的信誉为Linux和Windows Server应用发布新功能。 在本套课程中,我们将全面的讲解Docker技术栈,从环境安装到容器、镜像操作以及生产环境如何部署开发的微服务应用。本课程由黑马程序员提供。 &nbsp; &nbsp; 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
目录
相关文章
|
监控 网络协议 Perl
[原创]结合案例深入解析orphan socket产生与消亡(一)
本文看点:结合服务器运行案例和TCP代码分析orphan socket产生与消亡以及对系统的影响。精彩的部分在(二)细节分析章节。 ##问题背景 tengine服务器发生过多次orphan socket数量很多的情况,例如有一次使用ss -s命令查看: ``` $ss -s T
8872 0
|
机器学习/深度学习 数据采集 数据可视化
R语言 一种功能强大的数据分析、统计建模 可视化 免费、开源且跨平台 的编程语言
R语言 一种功能强大的数据分析、统计建模 可视化 免费、开源且跨平台 的编程语言
766 1
|
运维 网络协议 算法
不为人知的网络编程(十六):深入分析与解决TCP的RST经典异常问题
本文将从TCP的RST技术原理、排查手段、现网痛难点案例三个方面,自上而下、循序渐进地给读者带来一套完整的分析方法和解决思路。
369 0
EMQ
|
JSON Linux 网络性能优化
MQTT 5.0 报文解析 02:PUBLISH 与 PUBACK
本文将介绍在 MQTT 中用于传递应用消息的 PUBLISH 报文以及它的响应报文。不管是客户端向服务端发布消息,还是服务端向订阅端转发消息,都需要使用 PUBLISH 报文。决定消息流向的主题、消息的实际内容和 QoS 等级,都包含在 PUBLISH 报文中。
EMQ
777 82
MQTT 5.0 报文解析 02:PUBLISH 与 PUBACK
|
11月前
|
存储 前端开发 开发者
深入了解 Sass 和 SCSS:CSS 预处理器的强大功能
Sass(Syntactically Awesome Stylesheets)是一个强大的 CSS 预处理器,为开发者提供了诸多高级特性,如变量、嵌套、混合、继承等,简化了 CSS 的编写和管理。SCSS 是 Sass 3 引入的新语法,完全兼容 CSS3,并增强了 Sass 的动态功能。本文详细介绍了 Sass 和 SCSS 的核心特性及其在实际开发中的应用,如变量定义、嵌套规则、混合、继承、以及常用的操作符与扩展功能。
404 0
|
监控 网络协议 应用服务中间件
动图图解!收到RST,就一定会断开TCP连接吗?
动图图解!收到RST,就一定会断开TCP连接吗?
917 0
|
应用服务中间件 nginx
Nginx源码阅读:nginx_shmtx共享互斥锁(进程锁)
Nginx源码阅读:nginx_shmtx共享互斥锁(进程锁)
250 0
|
数据采集 监控
IEC104 主站/客户端模拟器
`IEC104`主站/客户端模拟器,提供多主站/客户端模拟,单连接多站模拟,全面支持信息对象解析和自定义`APCI`参数。具备自动时钟同步、总召命令功能,直观的命令执行工具,通信帧监视及导出,ASDU管理,实时曲线绘制,内置校验和计算等实用工具。适用于电力系统监控调试,官网下载:[http://www.redisant.cn/iec104client](http://www.redisant.cn/iec104client)。
545 0
IEC104 主站/客户端模拟器
|
存储 Kubernetes 调度
K8s Pod亲和性、污点、容忍度、生命周期与健康探测详解(上)
本文全面探讨了Kubernetes集群中Pod的四种关键机制——Pod亲和性、污点(Taints)、容忍度(Tolerations)、生命周期以及健康探测,为读者提供了深入理解并有效应用这些特性的指南。
|
运维 网络协议 Unix
Linux终端(Terminal)与控制台(Console)的区别
Linux终端(Terminal)与控制台(Console)的区别
704 0