深入Linux网络子系统03:调用 listen 之后,内核发生了哪些事?

Linux——网络通信 包括多种机制,如管道、消息队列、信号、共享内存和信号量。这些机制都依赖于Linux内核提供的支持,用于实现不同进程之间的数据交换和同步。然而,这些通信方式在本地进程间通信中非常有用,但无法直接用于跨机器间的通信。 阅读详情


前言

socket()创建套接字、bind()绑定 IP 与端口。完成 bind 之后,socket 还只是处于CLOSED状态,并不能接收客户端连接,想要对外提供服务,必须调用listen()。很多人都知道listen开启监听,但对于下面的问题是否清楚:

  1. listen在内核中完成了哪些工作?
  2. backlog参数限制的哪一个队列?
  3. 半连接、全连接队列什么区别?

一、系统调用入口

1.1 函数原型

int ret = listen(fd, backlog);

参数详解:

  • fd:socket()调用返回的文件描述符
  • backlog:全连接队列的最大长度

返回值: 成功返回 0;失败返回‑1

1.2 业务层简单示例

// 创建socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);

struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
// 绑定所有网卡
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
// 绑定IP和端口
int ret = bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

// 打开监听
ret = listen(sockfd, 128);

二、关键数据结构

// include/net/request_sock.h
struct request_sock_queue 
{
    // 当前半连接数
    atomic_t		qlen;
    atomic_t		young;
    // 全连接队列指针
    struct request_sock	*rskq_accept_head;
    struct request_sock	*rskq_accept_tail;
};
  1. 全连接队列:完成三次握手,状态切换为 ESTABLISHED 并且没有被accept()取走的的socket。backlog参数限制的是全连接队列的最大长度,同时全连接队列上限不能超过系统全局参数net.core.somaxconn,最终队列大小取「backlog 和 somaxconn 的较小值」
  2. 半连接队列:收到 SYN 后、第三次握手 ACK 到来前,服务端创建一个struct request_sock记录对方信息存入该队列,并发送 SYN+ACK,等待客户端最后一次 ACK
  3. 结构体字段释义:
  • qlen:所有半连接的数量
  • young:半连接中尚未经过第一次 SYN+ACK 重传定时器处理的数量
  • rskq_accept_head/rskq_accept_tail:全连接队列指针
  1. 二者行为表现后续三次握手详细讲解

划重点:TCP三次握手是内核协议栈自动完成,listen()只负责切换监听状态、初始化队列;accept()并不参与握手,仅仅是把已经握手完成的连接从全连接队列取出交给应用程序。

三、listen调用过程

3.1 系统调用入口

关键行为:

  1. 根据文件描述符 fd 查找到内核socket对象;
  2. 做参数校验,如果应用程序传入的 backlog 大于somaxconn,直接截断为somaxconn;
  3. 调用协议层真正的listen实现,即inet_listen。

源码(节选):

// net/socket.c
SYSCALL_DEFINE2(listen, int, fd, int, backlog)
{
    struct socket *sock;
    int somaxconn;
    // 由 fd 获取 监听的 socket
    sock = sockfd_lookup_light(fd, &err, &fput_needed);
    // backlog不能超过core.sysctl_somaxconn
    somaxconn = sock_net(sock->sk)->core.sysctl_somaxconn;
    if ((unsigned int)backlog > somaxconn)
			backlog = somaxconn;
    // 实际调用 inet_stream_ops 中的 inet_listen
    sock->ops->listen(sock, backlog);
}

3.2 inet_listen

关键行为:

  1. socket 层前置检查:只允许流式 TCP 套接字,套接字必须处于未连接状态;
  2. 如果 socket 还没有进入 LISTEN 状态,调用inet_csk_listen_start正式开启监听;
  3. 如果 socket 已经是 LISTEN 状态,仅更新全连接队列上限。

源码(节选):

// net/ipv4/af_inet.c
int inet_listen(struct socket *sock, int backlog)
{
    struct sock *sk = sock->sk;
    unsigned char old_state;
    // socket 必须是未连接状态(排除已connect()的客户端socket、已accept()的socket 等)
    // 只对 SOCK_STREAM 流式 TCP 套接字生效
    if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
        goto out;
    old_state = sk->sk_state;
    // sock 的状态必须是 TCP_CLOSE 和 TCP_LISTEN
    if (!((1 << old_state) & (TCPF_CLOSE | TCPF_LISTEN)))
        goto out;
    if (old_state != TCP_LISTEN)
        // 真正将 socket 变为监听状态的核心函数
        inet_csk_listen_start(sk, backlog);
    // 不管新 listen 还是已 listen,都会走到这里
    sk->sk_max_ack_backlog = backlog;
out:
    return err;
}

3.3 inet_csk_listen_start

关键行为:

  1. 调用reqsk_queue_alloc初始化连接队列`icsk_accept_queue;
  2. 设置全连接队列上限sk_max_ack_backlog;sk_ack_backlog为当前全连接队列长度,初始为 0;
  3. 将 sock 内核状态修改为TCP_LISTEN,开启监听;
  4. 如果调用 bind 绑定过端口,这里就是那个端口;否则内核自动分配一个空闲端口,把监听 sock 加入监听哈希表。

源码(节选):

// net/ipv4/inet_connection_sock.c
int inet_csk_listen_start(struct sock *sk, int backlog)
{
    struct inet_connection_sock *icsk = inet_csk(sk);
    struct inet_sock *inet = inet_sk(sk);
    // 初始化全连接队列
    reqsk_queue_alloc(&icsk->icsk_accept_queue);
    // 全连接队列长度上限
    sk->sk_max_ack_backlog = backlog;
    // 当前全连接队列的长度,初始为0
    sk->sk_ack_backlog = 0;
    // 将sock状态改为TCP_LISTEN
    sk_state_store(sk, TCP_LISTEN);
    // 如果调用 bind 绑定过端口,这里就是那个端口;否则内核自动分配一个空闲端口
    if (!sk->sk_prot->get_port(sk, inet->inet_num))
    {
        inet->inet_sport = htons(inet->inet_num);
        // 将监听 sock 加入 listening_hash
        sk->sk_prot->hash(sk);
    }
}

3.3 reqsk_queue_alloc

关键行为:初始化全连接队列链表

源码(节选):

// net/core/request_sock.c
void reqsk_queue_alloc(struct request_sock_queue *queue)
{
    queue->rskq_accept_head = NULL;
}

四、相关内核参数查看命令

# 查看系统somaxconn配置
cat /proc/sys/net/core/somaxconn
# 查看半连接队列配置
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
# 查看syn‑cookie开关状态
cat /proc/sys/net/ipv4/tcp_syncookies

五、总结

listen()方法主要做了以下几件事情:

  1. 将 socket 从连接创建状态(TCP_CLOSE)切换为监听状态(TCP_LISTEN),使其能够接收客户端连接请求,初始化全连接队列,TCP三次握手过程由内核自动完成。
  2. backlog 控制全连接队列上限,最终生效值会被somaxconn系统参数截断
  3. 如果 socket 已经是监听状态,则只允许调整 backlog 大小
  4. TCP 三次握手由内核自动完成;握手完成的连接放到全连接队列排队,等待accept()取出
Ubuntu 16.04 安装 Docker 17.03 实战指南:内核模块、存储驱动与镜像加速 Docker 是基于 Linux 内核特性(如 cgroups、namespaces 和 union filesystem)构建的容器运行时,其稳定运行依赖内核版本、文件系统模块支持及 cgroup 挂载完整性。Ubuntu 16.04 虽满足最低内核要求(≥3.10),但默认未启用 aufs 模块且 cgroup v1 子系统挂载不全,导致 docker daemon 启动失败;同时官方源中 docker.io 版本陈旧(1.12.6),缺乏对 healthcheck、--mount 等现代功能的支持。本 阅读详情

相关推荐

Linux性能诊断实战:TCP协议栈与内核参数的动态反馈分析

Linux性能本质是内核、网络协议栈与真实负载共同作用的动态过程。TCP作为最复杂的内核子系统之一,其行为受协议状态机、内存管理、中断调度和硬件交互多重制约;而Kernel参数并非独立开关,而是嵌套在系统方程中的调节变量——例如tcp_tw_reuse生效依赖window_scaling与远端SYN-ACK选项,tcp_rmem上限受tcp_mem压力阈值反向约束。这种强耦合性决定了性能优化必须基于交叉验证(ss -i /proc/net/snmp/tcpdump)和向下穿透式诊断(从API延迟回溯至irq

weixin_33725722的博客 345

【Linux网络编程】TCP网络编程中connect listen和accept三者之间的关系

00. 目录 文章目录00. 目录01. TCP服务端和客户端流程02. connect函数03. listen函数04. 三次握手05. accept函数06. 附录 01. TCP服务端和客户端流程 02. connect函数 对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三次握手,而这个连接的过程是由内核完成,不是这个函数完成的,这个函数的作用仅仅是通知 Linux 内核,让 Linux 内核自动完成 TCP 三次握手连接,最后把连接的结果返回给这个函数的

C/C++软件工程师、嵌入式软件工程师、物联网研发工程师、C/C++讲师、物联网讲师、嵌入式讲师、鸿蒙讲师---欢迎大家一起交流(私信添加博主微信) 1627

Linux通用系统高危漏洞修复案例

2024年3月28日,监 Linux kernel权限提升漏洞(CVE-2024-1086)的PoC/EXP在互联网上公开,该漏洞的CVSS评分为7.8,目前漏洞细节已经公开披露,美国网络安全与基础设施安全局(CISA)6月1日也更新了其已知漏洞(KEV)目录,要求联邦机构在 2024 年 6 月 20 日之前打上补丁,修复追踪编号为 CVE-2024-1086 的 Linux 内核权限提升漏洞。由于该漏洞的公开性,攻击者可能会利用这个漏洞进行恶意攻击,因此建议尽快采取修复措施。

企业实战系列集 ●●● https://ximenjianxue.blog.youkuaiyun.com 4941

Linux下的网络编程

所谓网络编程,就是使进程在不同的终端上依然可以相互通信的一个解决方案,原始的网络编程就是将进程实现业务需要使用的字节流发送到其他终端上的进程进行处理,以实现用户通信。

m0_57931542的博客 1230

《网络安全自学教程》- 工作中常用的Linux命令

100个Linux常用命令及常用操作。

wangyuxiang946的博客 9402

Ceph入门到精通-Linux内核网络参数优化小结

第 2 个比特位为 1 时,表示作为服务器时支持 TFO,所以当 tcp_fastopen 的值为 3 时(比特为 0x11)就表示完全支持 TFO 功能。强烈不建议启用tcp_tw_recycle,会导致数据错乱,4.12内核已去掉这个参数。上述操作后,依然溢出的,会被丢弃,建议启用rst复位以通知客户端。超过下述参数,就不走TIME_WAIT直接关闭,默认值18w。3 accept后,服务器过忙,队列等待超出被丢弃。调整下述参数,再超出就会RST,默认是8192。1 tcp建连,降低客户端超时时间。

隔壁老瓦的专栏 668

Linux 网络编程

同样地,接收主机如果是小端字节序的,接到16位的源端口号也要做字节序的转换。因此,服务器从accept()返回后立刻调用read(),读socket就像读管道一样,如果没有数据到达就阻塞等待,这时客户端调用write()发送请求给服务器,服务器收到后从read()返回,对客户端的请求进行处理,在此期间客户端调用read()阻塞等待服务器的应答,服务器调用write()将处理结果发回给客户端,再次调用read()阻塞等待下一条请求,客户端收到后从read()返回,发送下一条请求,如此循环下去。

qq_45030264的博客 1039

146Linux 网络编程2,创建服务器端客户端,socket,bind,listen,accept,connect,C/S模型-TCP,出错封装函数,TCP状态转换图,端口复用setsockopt

例如上一节的UDP段格式,地址0-1是16位的源端口号,如果这个端口号是1000(0x3e8),则地址0是0x03,地址1是0xe8,也就是先发0x03,再发0xe8,这16位在发送主机的缓冲区中也应该是低地址存0x03,高地址存0xe8。发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存,因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址。本质为内核借助缓冲区形成的伪文件。

hunandede的博客 1165

Linux网络编程:Socket

概念 Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。既然是文件,自然可以使用文件描述符引用套接字。Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket

高新园养鸡场 352

光学仿真案例(1) 铌酸锂非线性波导仿真模拟 FDTD仿真

在过去几年里,许多集成光子学应用往往需要多个相干可调谐光源,而且从外部激光器引入额外的光输入增加了芯片封装的复杂性,如果使用基于III-V族、氮化硅(SiN)或铌酸锂(LNO)的集成光源又大大增加了制造复杂性和成本。然而,通过非线性谐波相互作用产生的光源不需要额外的制造工艺来产生增益、反馈和外部电路,即可支持许多应用。在下面的案例中,演示了一种基于Luo[1]的铌酸锂波导频率转换集成系统。该案例主要仿真了利用温度调制实现高效相位匹配的铌酸锂纳米光子波导的二次谐波产生。

q895235273的博客 4145

22版本Altium Designer导出生产文件的规范化操作指南

本文详细介绍了PCB设计中的Gerber文件导出流程及相关文件生成方法。主要内容包括:1)Gerber文件导出的5个关键设置步骤;2)NC钻孔文件的导出方法;3)坐标文件的生成方式;4)文件打包规范要求必须包含Gerber文件、NC钻孔文件和坐标文件;5)文件提交的两种方式(新手推荐提交原始工程文件,量产推荐仅提交Gerber文件包)。文章还特别强调了单位一致性、精度验证等注意事项,为PCB设计文件的规范输出提供了完整指导。

qq_56506325的博客 1060

【Linux网络编程】Socket编程

socket概念定义IP地址文件类型Linux中文件类型占用空间:普通文件 目录 软链接不占用空间:字符设备 块设备 管道 套接字管道全双工模式预备知识网络字节号IP地址转换函数sockaddr数据结构类型转换定义传参套接字函数Socket函数bind函数Listenaccept函数connect函数 客户端函数模型流程图 定义 Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。 既然是文件,那么理所当然的,我们可以使用文件描述符引

allensu374125056的博客 433

【深度学习新浪潮】Nano Banana(Gemini 2.5 Flash Image)技术解析与开发者实操指南

重新定义了 AI 图像生成的产业标准。它不仅是“平民化的视觉工厂”,更是推动内容生产从“创意试错”转向“精准制造”的关键工具。未来,随着视频生成能力的加入(谷歌计划支持“文字指令 - 动态场景 - 多平台适配”全流程),Nano Banana 有望进一步颠覆影视、广告等领域的创作范式。:批量生成多场景图,模特姿态自然,服装褶皱和材质细节高度一致,节省 90% 拍摄成本。模型的非正式代号,是一款革命性的 AI 图像生成与编辑工具,主打。:生成细节丰富的厨师特写,光影自然,皮肤纹理和刀具反光逼真。

专注:太空算力、AI infra、异构调度、大模型成本工程 894

Linux网络编程04:网络基础(万字图文解析)

本文补充了计算机网络基础知识和Linux网络命令。主要内容包括:1)传输层相关知识,如端口绑定规则(一个进程可绑定多个端口,但一个端口不能被多个进程同时绑定);2)常用网络命令的使用,包括netstat查看网络状态、pidof获取进程ID、ifconfig显示网卡信息;3)协议报头的结构化特性;4)网络IO接口实际是数据拷贝接口,数据在内核缓冲区和用户层之间传输的机制。文章还提及TCP可靠传输的实现原理,适合作为计算机网络基础知识的补充阅读。

2301_80030580的博客 867

RT-DERT安卓部署教程

这个文档中的代码案例,实现了拍照和选图识别,两个功能,可以看效果图。UI做了美化:显示类别机和数量。代码下载地址:通过网盘分享的文件:android_rtdert.zip链接: https://pan.baidu.com/s/1k6e-d_lov1OPgUmit5i_cA?pwd=hpfi 提取码: hpfi–来自百度网盘超级会员v8的分享。

AIOT魔法师 196

分数间隔均衡器的基本原理及应用(使用Matlab)

通过对接收信号进行加权求和,分数间隔均衡器可以抵消信号在传输过程中受到的频率响应失真。Matlab提供了丰富的工具和函数,可以方便地实现分数间隔均衡器,并进行性能评估。分数间隔均衡器(Fractional Interval Equalizer)是一种数字信号处理技术,用于均衡带通信道中的信号失真。分数间隔均衡器的基本原理是通过对接收信号进行加权求和,以抵消信号在传输过程中受到的频率响应失真。分数间隔均衡器在通信系统中具有广泛的应用。最后,我们通过比较均衡后的信号和发送数据的阈值来进行解调,并计算误码率。

2301_79325657的博客 684

linux内核优化哪些参数,Linux内核网络参数优化小结

tcp建连优化1 tcp建连,降低客户端超时时间net.ipv4.tcp_syn_retries = 62 tcp建连,服务端避免syn攻击netstat -s | grep "SYNs to LISTEN" 1192450 SYNs to LISTEN sockets dropped可以考虑增大syn队列net.ipv4.tcp_max_syn_backlog = 1024超过上述队列后,启用s...

weixin_42318815的博客 324

前后端面试题【目录】

工作时候扭螺丝,面试时候造火箭,现状就是如此。不管是背八股文,还是掌握了知识的精华,对答如流才是硬道理。本专栏就是要集具前端精华,规范、算法、架构、封装、原理等一并汇集,让前端的小伙伴有个思路。, 欢迎一起交流,并非常期望您能够提供有价值的学习资料。

大剑师兰特的GIS世界 1837

TCP全连接队列

全连接队列(accept队列):Linux内核为LISTEN监听套接字维护的队列。已经完成三次握手、状态为 ESTABLISHED,但是应用程序还没有调用accept()取走的TCP连接。半连接队列(SYN队列),存放只收到SYN、还没完成三次握手(SYN_RCVD)的连接。

weixin_73914025的博客 345

AI上推荐 之 多任务loss优化(自适应权重篇)

1. 写在前面 在多任务学习中,往往会将多个相关的任务放在一起来学习。例如在推荐系统中,排序模型同时预估候选的点击率和浏览时间。相对于单任务学习,多任务学习有以下优势: 多个任务共享一个模型,占用内存量减少; 多个任务一次前向计算得出结果,推理速度增加; 关联任务通过共享信息,相互补充,可以提升彼此的表现。 前面的一篇文章,主要是从模型结构的角度聊了下多任务学习,介绍了工业界非常经典也常用的MMOE模型,然而,对于多任务学习, Loss的优化也非常重要, 因为我们知道通过多个目标去指导网络进行统一的训练

Miracle8070 1万+

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

listen()函数中backlog参数分析

 listen()函数中backlog参数分析 分类: Linux-net 2014-03-19 19:57 2178人阅读 评论(1) 收藏 举报 listenbacklog连接数目 目录(?)[+] 背景知识总结实例分析1实例分析2 背景知识 Unix网络编程描述如下: 总结 0. accept()函数不参与三

u011116193的专栏 556

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

当netstat失效时,如何用tcpdump揪出内核级Rootkit隐藏连接

在Linux系统安全排查中,网络连接状态监控是基础且关键的环节。传统工具如netstat、ss通过读取/proc文件系统等内核接口来展示连接信息,其原理是依赖操作系统提供的抽象层。然而,高级内核级Rootkit通过挂钩系统调用或篡改内核数据结构,能轻易过滤和隐藏自身连接,导致这些工具失效,形成“一切正常”的假象。此时,tcpdump的技术价值凸显,它工作在数据链路层,直接抓取网卡原始数据包,绕过了可能被污染的上层抽象,为发现隐匿通信提供了底层视角。在应急响应和主机入侵检测(HIDS)实践中,结合tcpdum

weixin_34414650的博客 368

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

linux net.core.somaxconn,linux中内核的一个不错的参数somaxconn

导读:在linux中,/proc/sys/net/core/somaxconn这个参数,linux中内核的一个不错的参数somaxconn看下其解析:对于一个TCP连接,Server与Client需要通过三次握手来建立网络连接.当三次握手成功后,我们可以看到端口的状态由LISTEN转变为ESTABLISHED,接着这条链路上就可以开始传送数据了.每一个处于监听(Listen)状态的端口,都有自己的...

weixin_34297395的博客 1557

理解 Linux 动态库加载:LD_LIBRARY_PATH

flyfish

二分掌柜的 399
上一篇: 深入Linux网络子系统02:bind()到底做了什么?
下一篇: 深入Linux网络子系统04:accept如何从全连接队列取出TCP连接
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值