php 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<

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值