通常我们发现网络性能不好的时候,第一个冒出来的想法不是tcpdump抓包看看?
然而,抓包只是一个手段,只能查看端到端的整体情况。我们有时候要更多地注意这些包还可能丢在了端节点上,再进一步,进行一次灵魂拷问:这些丢的包,到底丢哪了?
彤哥的答案是:有缓存的地方就有可能丢包!
大体来看,网络收发过程中有三个缓存,网卡收发包时通过DMA方式交互的RingBuffer(即RX/TX 队列、驱动buffer)、内核协议栈sk_buffer(即qdisc队列、backlog)、socket buffer (即平常所说的TCP/UDP收发缓存)。
要解释这三个缓存的来龙去脉,就要先来分析网络数据收发的流程。以接收数据为例:
大概明白了数据收发的原理,那我们来看如何分析网络丢包,原理无非就是缓存“不够”,包就丢了。当然,缓存“不够”,不一定是根因,有可能是因为CPU处理不过来等其它原因,因此,我们在实际情况中需要灵活地处理。废话不多说,具体的做法是三段排除法。
第一段:ringBuffer是否太小?
当驱动处理速度跟不上网卡收包速度时,驱动来不及分配缓冲区,NIC接收到的数据包无法及时写到sk_buffer(由网卡驱动直接在内核中分配的内存,并存放数据包,供内核软中断的时候读取),就会产生堆积,当NIC内部缓冲区写满后,就会丢弃部分数据,引起丢包。这部分丢包为rx_fifo_errors,在 /proc/net/dev中体现为fifo字段增长,在ifconfig中体现为overruns指标增长。可以通过“ethtool ‐g eth0 ”查看ring buffer的大小设置,通过 ethtool -G eth rx 8192 tx 8192可以进行缓存大小配置。
第二段:sk_buffer是否太小?
sk_buffer通过参数“net.core.netdev_max_backlog”指定,默认大小是 1000。netdev_max_backlog 表示接收包队列(网卡收到还没有进行协议的处理队列),一般情况下一个cpu core一个队列,可以通过“/proc/net/softnet_stat”进行查看。如果第二列增加就表示这个队列溢出了,需要改大。
第三段:socket buffer是否太小?
内核收到包后,会给对应的socket,每个socket会有 sk_rmem_alloc/sk_wmem_alloc/sk_omem_alloc、sk_rcvbuf(bytes)来存放包。由于内核协议栈在数据发送过程上中,如果socket buffer不够时,会发送EAGAIN消息反压;在数据接收时,flow control会通告接收窗口,因此这个缓存中的数据通常不会丢失。
有了上述三段式排除法,就可以大致分析数据包丢哪里了。当然,还有其它的参数也可能影响,比如“net.core.netdev_budget”,表示 软中断获取的CPU时间。一般默认软中断只绑定在CPU0上,如果包的数量巨大的话会导致 CPU0利用率 100%(主要软中断si),这个时候可以检查文件 /proc/net/softnet_stat 的第三列 或者 RX overruns 是否在持续增大。
另外,还有两个小工具/技巧,一是可以通过Dropwatch来查看丢包点:”dropwatch -l kas (-l 加载符号表) // 丢包点位置等于 ip_rcv地址+ cf(偏移量)“,二是
在配置tcpdump命令时,进行如下配置,可使得即使包被丢失时,也能被监控到:”sudo iptables -A INPUT -p tcp –destination-port 端口号 -j DROP“
然而,抓包只是一个手段,只能查看端到端的整体情况。我们有时候要更多地注意这些包还可能丢在了端节点上,再进一步,进行一次灵魂拷问:这些丢的包,到底丢哪了?
彤哥的答案是:有缓存的地方就有可能丢包!
大体来看,网络收发过程中有三个缓存,网卡收发包时通过DMA方式交互的RingBuffer(即RX/TX 队列、驱动buffer)、内核协议栈sk_buffer(即qdisc队列、backlog)、socket buffer (即平常所说的TCP/UDP收发缓存)。
要解释这三个缓存的来龙去脉,就要先来分析网络数据收发的流程。以接收数据为例:
- 网卡收到数据包,将数据帧以DMA的方式存放到ringBuffer中,然后向CPU发起硬中断请求。CPU响应硬中断请示,调用中断处理函数从而发起软中断请求。
- 网卡驱动程序将数据包从ringBuffer拷贝到sk_buffer中。
- 通知内核处理。
- 经过TCP/IP协议逐层处理(我理解指的就是链路层、网络层和传输层),将处理后的数据拷贝到socket buffer中。
- 应用程序通过read()从socket buffer读取数据。
大概明白了数据收发的原理,那我们来看如何分析网络丢包,原理无非就是缓存“不够”,包就丢了。当然,缓存“不够”,不一定是根因,有可能是因为CPU处理不过来等其它原因,因此,我们在实际情况中需要灵活地处理。废话不多说,具体的做法是三段排除法。
第一段:ringBuffer是否太小?
当驱动处理速度跟不上网卡收包速度时,驱动来不及分配缓冲区,NIC接收到的数据包无法及时写到sk_buffer(由网卡驱动直接在内核中分配的内存,并存放数据包,供内核软中断的时候读取),就会产生堆积,当NIC内部缓冲区写满后,就会丢弃部分数据,引起丢包。这部分丢包为rx_fifo_errors,在 /proc/net/dev中体现为fifo字段增长,在ifconfig中体现为overruns指标增长。可以通过“ethtool ‐g eth0 ”查看ring buffer的大小设置,通过 ethtool -G eth rx 8192 tx 8192可以进行缓存大小配置。
第二段:sk_buffer是否太小?
sk_buffer通过参数“net.core.netdev_max_backlog”指定,默认大小是 1000。netdev_max_backlog 表示接收包队列(网卡收到还没有进行协议的处理队列),一般情况下一个cpu core一个队列,可以通过“/proc/net/softnet_stat”进行查看。如果第二列增加就表示这个队列溢出了,需要改大。
第三段:socket buffer是否太小?
内核收到包后,会给对应的socket,每个socket会有 sk_rmem_alloc/sk_wmem_alloc/sk_omem_alloc、sk_rcvbuf(bytes)来存放包。由于内核协议栈在数据发送过程上中,如果socket buffer不够时,会发送EAGAIN消息反压;在数据接收时,flow control会通告接收窗口,因此这个缓存中的数据通常不会丢失。
有了上述三段式排除法,就可以大致分析数据包丢哪里了。当然,还有其它的参数也可能影响,比如“net.core.netdev_budget”,表示 软中断获取的CPU时间。一般默认软中断只绑定在CPU0上,如果包的数量巨大的话会导致 CPU0利用率 100%(主要软中断si),这个时候可以检查文件 /proc/net/softnet_stat 的第三列 或者 RX overruns 是否在持续增大。
另外,还有两个小工具/技巧,一是可以通过Dropwatch来查看丢包点:”dropwatch -l kas (-l 加载符号表) // 丢包点位置等于 ip_rcv地址+ cf(偏移量)“,二是
在配置tcpdump命令时,进行如下配置,可使得即使包被丢失时,也能被监控到:”sudo iptables -A INPUT -p tcp –destination-port 端口号 -j DROP“