网卡缓冲值问题-续
使用ip
命令查看网状态。ifconfig
命令已多年不再更新,从CentOS7开始,发行版的mini系统已不再自带ifconfig
工具,取而代之的是ip
命令。
其实质是iproute2
套件取代了net-tools
套件,ifconfig
便是后者中一个工具。两者的简单对比,从网上找到张图:

查看网卡统计数据
使用ip -s link
查看link的统计数据:

以上各个统计指标的含义为:
指标 | 含义 |
---|---|
errors |
网线损坏或者网卡协商模式错误或者速率不匹配导致的错误。 |
dropped |
iptables 或者其他规则或者内核读取网卡数据的buffer满导致的丢包。 |
overrun |
网卡的驱动buffer满导致的overruns。 |
carrier |
网线接触不良或者网线损坏或者交换机故障。 |
collsns |
一般而言是0。若非零,代表双工模式的协商问题。若数值很小且不再增长,代表只是刚接入的时候故障,现已正常。 |
网卡buffer
网卡buffer设置
上篇已提到,在网卡统计中出现overruns时,需要确定是否是由于网卡buffer设置不当导致的。使用ethtool -g eth0
查看当前设置,具体查看上篇。

值得说明的是,此处的数值大小并不是代表可以缓冲多大的数据,而是指可以缓冲的数据帧(frame)的数量。
网卡buffer工作原理
网卡buffer会采用环形缓冲(ring buffer)的形式,其也是FIFO的一种实现方式,设计非常简单,也比较稳定,在较为底层的数据传输中使用广泛。其工作原理大致如下图所示,具体可以查看wiki。

其他注意事项
有两个需要注意的地方:
buffer设置
使用ethtool -G eth0 rx 256
设置接收buffer后,NIC会有个down/up的操作,会出现瞬时的断网,在网卡吞吐量较大时,副作用明显。
另外每次系统重启之后,该设置会失效,需要将该设置命令写入开机启动脚本/etc/rc.local
中。
延迟
网卡的buffer并不是越多越好,在接入带宽较低的情况下,设置很大的带宽导致数据包传输的延迟。
如在1Mbps接入带宽,MTU=1500字节的情况下,设置tx buffer=256帧。假设每个包刚好都是1500字节,则填充满buffer需要1500*256=384,000字节,也就是3,072,000比特。1Mbps每秒最多传输1024*1024=1,048,000比特的数据,当最新的一个包进入到buffer的时候,需要3s才能发送出去,会造成3s的数据延迟。
在有巨型帧(Jumbo Frame)时,每个帧长度更长,导致需要更多的时间充满buffer,延迟更久。
内核参数
以上所说的buffer均指的是网卡及其驱动程序所使用的buffer,另外还有个内核使用的buffer,用以从网卡读取数据帧所用,在内核参数中设置。
BQL
此处有个概念叫starvation,中文有翻译成饿死,即网卡从ring buffer读取数据的时候,buffer中没有数据,次时设备空转一次,造成浪费,称为starvation。避免starvation是个十分复杂的问题,一个有效的办法便是增大ring buffer的长度,使得内核可以存放跟多的数据到队列中,减少starvation产生的概率。
但是增大ring buffer长度可能会导致上节中描述的发包延迟现象,因此出现了BQL。
在3.3.0内核之后,linux 内核自身会通过字节队列限制(Byte Queue Limits,BQL)自动调整数据包排队的数量,以避免在ring buffer中排队的数据包太多导致的发送延迟。实质上是在IP栈和drive queue之间加了一层,对进入drive queue的数据进行调度,避免队列满而导致延迟,同时也减少buffer长度。
这边有些相关资料,留作备份,暂时看不懂,也没需求,先不看了。
https://www.linuxplumbersconf.org/2012/wp-content/uploads/2012/08/bql_slide.pdf
http://www.linuxplumbersconf.org/2011/ocw/sessions/171
/proc/sys/net
IP栈允许到ring buffer排队的数据包数量可以在proc/sys/net/core/netdev_max_backlog
中设置。另外在/proc/sys/net
和/etc/sysctl.conf
中有很多网络相关的内核参数,具体可以参考这篇博文。
参数(路径+文件) | 描述 | 默认值 | 优化值 |
---|---|---|---|
/proc/sys/net/core/rmem_default | 默认的TCP数据接收窗口大小(字节)。 | 229376 | 256960 |
/proc/sys/net/core/rmem_max | 最大的TCP数据接收窗口(字节)。 | 131071 | 513920 |
/proc/sys/net/core/wmem_default | 默认的TCP数据发送窗口大小(字节)。 | 229376 | 256960 |
/proc/sys/net/core/wmem_max | 最大的TCP数据发送窗口(字节)。 | 131071 | 513920 |
/proc/sys/net/core/netdev_max_backlog | 在每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许送到队列的数据包的最大数目。 | 1000 | 2000 |
/proc/sys/net/core/somaxconn | 定义了系统中每一个端口最大的监听队列的长度,这是个全局的参数。 | 128 | 2048 |
/proc/sys/net/core/optmem_max | 表示每个套接字所允许的最大缓冲区的大小。 | 20480 | 81920 |
/proc/sys/net/ipv4/tcp_mem | 确定TCP栈应该如何反映内存使用,每个值的单位都是内存页(通常是4KB)。第一个值是内存使用的下限;第二个值是内存压力模式开始对缓冲区使用应用压力的上限;第三个值是内存使用的上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的BDP可以增大这些值(注意,其单位是内存页而不是字节)。 | 94011 125351 188022 | 131072 262144 524288 |
/proc/sys/net/ipv4/tcp_rmem | 为自动调优定义socket使用的内存。第一个值是为socket接收缓冲区分配的最少字节数;第二个值是默认值(该值会被rmem_default覆盖),缓冲区在系统负载不重的情况下可以增长到这个值;第三个值是接收缓冲区空间的最大字节数(该值会被rmem_max覆盖)。 | 4096 87380 4011232 | 8760 256960 4088000 |
/proc/sys/net/ipv4/tcp_wmem | 为自动调优定义socket使用的内存。第一个值是为socket发送缓冲区分配的最少字节数;第二个值是默认值(该值会被wmem_default覆盖),缓冲区在系统负载不重的情况下可以增长到这个值;第三个值是发送缓冲区空间的最大字节数(该值会被wmem_max覆盖)。 | 4096 16384 4011232 | 8760 256960 4088000 |
/proc/sys/net/ipv4/tcp_keepalive_time | TCP发送keepalive探测消息的间隔时间(秒),用于确认TCP连接是否有效。 | 7200 | 1800 |
/proc/sys/net/ipv4/tcp_keepalive_intvl | 探测消息未获得响应时,重发该消息的间隔时间(秒)。 | 75 | 30 |
/proc/sys/net/ipv4/tcp_keepalive_probes | 在认定TCP连接失效之前,最多发送多少个keepalive探测消息。 | 9 | 3 |
/proc/sys/net/ipv4/tcp_sack | 启用有选择的应答(1表示启用),通过有选择地应答乱序接收到的报文来提高性能,让发送者只发送丢失的报文段,(对于广域网通信来说)这个选项应该启用,但是会增加对CPU的占用。 | 1 | 1 |
/proc/sys/net/ipv4/tcp_fack | 启用转发应答,可以进行有选择应答(SACK)从而减少拥塞情况的发生,这个选项也应该启用。 | 1 | 1 |
/proc/sys/net/ipv4/tcp_timestamps | TCP时间戳(会在TCP包头增加12个字节),以一种比重发超时更精确的方法(参考RFC 1323)来启用对RTT 的计算,为实现更好的性能应该启用这个选项。 | 1 | 1 |
/proc/sys/net/ipv4/tcp_window_scaling | 启用RFC 1323定义的window scaling,要支持超过64KB的TCP窗口,必须启用该值(1表示启用),TCP窗口最大至1GB,TCP连接双方都启用时才生效。 | 1 | 1 |
/proc/sys/net/ipv4/tcp_syncookies | 表示是否打开TCP同步标签(syncookie),内核必须打开了CONFIG_SYN_COOKIES项进行编译,同步标签可以防止一个套接字在有过多试图连接到达时引起过载。 | 1 | 1 |
/proc/sys/net/ipv4/tcp_tw_reuse | 表示是否允许将处于TIME-WAIT状态的socket(TIME-WAIT的端口)用于新的TCP连接 。 | 0 | 1 |
/proc/sys/net/ipv4/tcp_tw_recycle | 能够更快地回收TIME-WAIT套接字。 | 0 | 1 |
/proc/sys/net/ipv4/tcp_fin_timeout | 对于本端断开的socket连接,TCP保持在FIN-WAIT-2状态的时间(秒)。对方可能会断开连接或一直不结束连接或不可预料的进程死亡。 | 60 | 30 |
/proc/sys/net/ipv4/ip_local_port_range | 表示TCP/UDP协议允许使用的本地端口号 | 32768 61000 | 1024 65000 |
/proc/sys/net/ipv4/tcp_max_syn_backlog | 对于还未获得对方确认的连接请求,可保存在队列中的最大数目。如果服务器经常出现过载,可以尝试增加这个数字。 | 2048 | 2048 |
/proc/sys/net/ipv4/tcp_low_latency | 允许TCP/IP栈适应在高吞吐量情况下低延时的情况,这个选项应该禁用。 | 0 | |
/proc/sys/net/ipv4/tcp_westwood | 启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化,对于WAN 通信来说应该启用这个选项。 | 0 | |
/proc/sys/net/ipv4/tcp_bic | 为快速长距离网络启用Binary Increase Congestion,这样可以更好地利用以GB速度进行操作的链接,对于WAN通信应该启用这个选项。 | 1 | 1 |
参考资料
QUEUEING IN THE LINUX NETWORK STACK
这边文章包罗了很多内容,非常值得细度。