Linux TCP队列相关参数的总结

在linux上做网络应用的性能优化时,一般都会对TCP相关的内核参数进行调节,特别是和缓冲、队列有关的参数 。很多文章会告诉你需要修改哪些参数,但我们经常是知其然而不知其所以然,每次照抄过来后,可能很快就忘记或混淆了它们的含义 。
下面我以server端为视角,从 连接建立、 数据包接收 和 数据包发送 这3条路径对参数进行归类梳理 。
一、连接建立

Linux TCP队列相关参数的总结

文章插图
 
简单看下连接的建立过程,客户端向server发送SYN包,server回复SYN+ACK,同时将这个处于SYN_RECV状态的连接保存到半连接队列 。客户端返回ACK包完成三次握手,server将ESTABLISHED状态的连接移入accept队列,等待应用调用accept() 。可以看到建立连接涉及两个队列:
  • 半连接队列,保存SYN_RECV状态的连接 。队列长度由net.ipv4.tcp_max_syn_backlog设置
  • accept队列,保存ESTABLISHED状态的连接 。队列长度为min(net.core.somaxconn,backlog) 。其中backlog是我们创建ServerSocket(intport,int backlog)时指定的参数,最终会传递给listen方法: #include int listen(int sockfd, int backlog); 如果我们设置的backlog大于net.core.somaxconn,accept队列的长度将被设置为net.core.somaxconn
另外,为了应对SYNflooding(即客户端只发送SYN包发起握手而不回应ACK完成连接建立,填满server端的半连接队列,让它无法处理正常的握手请求),Linux实现了一种称为SYNcookie的机制,通过net.ipv4.tcp_syncookies控制,设置为1表示开启 。简单说SYNcookie就是将连接信息编码在ISN(initialsequencenumber)中返回给客户端,这时server不需要将半连接保存在队列中,而是利用客户端随后发来的ACK带回的ISN还原连接信息,以完成连接的建立,避免了半连接队列被攻击SYN包填满 。对于一去不复返的客户端握手,不理它就是了 。
二、数据包的接收先看看接收数据包经过的路径:
Linux TCP队列相关参数的总结

文章插图
 
数据包的接收,从下往上经过了三层:网卡驱动、系统内核空间,最后到用户态空间的应用 。Linux内核使用sk_buff(socketkernel buffers)数据结构描述一个数据包 。当一个新的数据包到达,NIC(networkinterface controller)调用DMAengine,通过RingBuffer将数据包放置到内核内存区 。RingBuffer的大小固定,它不包含实际的数据包,而是包含了指向sk_buff的描述符 。当RingBuffer满的时候,新来的数据包将给丢弃 。一旦数据包被成功接收,NIC发起中断,由内核的中断处理程序将数据包传递给IP层 。经过IP层的处理,数据包被放入队列等待TCP层处理 。每个数据包经过TCP层一系列复杂的步骤,更新TCP状态机,最终到达recvBuffer,等待被应用接收处理 。有一点需要注意,数据包到达recvBuffer,TCP就会回ACK确认,既TCP的ACK表示数据包已经被操作系统内核收到,但并不确保应用层一定收到数据(例如这个时候系统crash),因此一般建议应用协议层也要设计自己的确认机制 。
【文章福利】需要C/C++ Linux服务器架构师学习资料加群812855908(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等)
Linux TCP队列相关参数的总结

文章插图
 
上面就是一个相当简化的数据包接收流程,让我们逐层看看队列缓冲有关的参数 。
1、网卡Bonding模式 当主机有1个以上的网卡时,Linux会将多个网卡绑定为一个虚拟的bonded网络接口,对TCP/IP而言只存在一个bonded网卡 。多网卡绑定一方面能够提高网络吞吐量,另一方面也可以增强网络高可用 。Linux支持7种Bonding模式:
详细的说明参考内核文档LinuxEthernet Bonding Driver HOWTO 。我们可以通过
cat/proc/net/bonding/bond0查看本机的Bonding模式:
Linux TCP队列相关参数的总结

文章插图
 
一般很少需要开发去设置网卡Bonding模式,自己实验的话可以参考这篇文档 。