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

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 从内核角度分析 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

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
目录
相关文章
|
1月前
|
Unix Linux
linux中在进程之间传递文件描述符的实现方式
linux中在进程之间传递文件描述符的实现方式
|
2月前
|
5G UED
5G NR中的寻呼过程是如何工作的?
【8月更文挑战第31天】
43 0
|
5月前
|
算法 Linux 调度
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
396 1
xenomai内核解析--xenomai与普通linux进程之间通讯XDDP(一)--实时端socket创建流程
|
5月前
|
Linux 调度 数据库
|
5月前
|
Linux 程序员 C语言
【linux基础I/O(一)】文件描述符的本质&重定向的本质
【linux基础I/O(一)】文件描述符的本质&重定向的本质
|
5月前
|
消息中间件 存储 网络协议
Linux IPC 进程间通讯方式的深入对比与分析和权衡
Linux IPC 进程间通讯方式的深入对比与分析和权衡
466 1
|
搜索推荐 Linux Windows
重编译LINUX内核调整SCTP参数的方法
经过在网络上用搜索引擎反复查找LINUX\SCTP相关资料,才发现LINUX内核2.6.18版本的SCTPINIT强制性添置ECN、ADAPTATION参赛,Forwrd TSN参数可以通过/proc/sys/net/sctp进行开关配置(0携带1不携带)。INIT_ACK消息则强制携带ECN、ADAPTATION参数。在找到这个结果之前经历了很多的误区,如修改LKSCTP、OPENSS7等。这些都是徒劳。
|
缓存 Linux C语言
库函数与系统调用之间的区别--扩展知识点1
库函数与系统调用之间的区别--扩展知识点1
190 0
|
网络协议 应用服务中间件 Linux