上一篇文章中《图解Linux网络包接收过程》,咱们梳理了在Linux系统下一个数据包被接收的整个过程。Linux内核对网络包的接收过程大体能够分为接收到RingBuffer、硬中断处理、ksoftirqd软中断处理几个过程。其中在ksoftirqd软中断处理中,把数据包从RingBuffer中摘下来,送到协议栈的处理,再以后送到用户进程socket的接收队列中。服务器
理解了Linux工做原理以后,还有更重要的两件事情。第一是动手监控,会实际查看网络包接收的总体状况。第二是调优,当你的服务器有问题的时候,你能找到瓶颈所在,并会利用内核开放的参数进行调节。网络
在正式内容开始以前,咱们先来了解几个Linux下监控网卡时可用的工具。数据结构
首先第一个工具就是咱们在上文中提到的ethtool
,它用来查看和设置网卡参数。这个工具其实自己只是提供几个通用接口,真正的实现是都是在网卡驱动中的。正由于该工具是由驱动直接实现的,因此我的以为它最重要。socket
该命令比较复杂,咱们选几个今天能用到的说ide
-i
显示网卡驱动的信息,如驱动的名称、版本等-S
查看网卡收发包的统计状况-g/-G
查看或者修改RingBuffer的大小-l/-L
查看或者修改网卡队列数-c/-C
查看或者修改硬中断合并策略实际查看一下网卡驱动:函数
# ethtool -i eth0 driver: ixgbe ......
这里看到个人机器上网卡驱动程序是ixgbe。有了驱动名称,就能够在源码中找到对应的代码了。对于ixgbe
来讲,其驱动的源代码位于drivers/net/ethernet/intel/ixgbe
目录下。ixgbe_ethtool.c
下都是实现的供ethtool使用的相关函数,若是ethtool哪里有搞不明白的,就能够经过这种方式查找到源码来读。另外咱们前文《图解Linux网络包接收过程》里提到的NAPI收包时的poll回调函数,启动网卡时的open函数都是在这里实现的。工具
网络管理工具ifconfig不仅是能够为网卡配置ip,启动或者禁用网卡,也包含了一些网卡的统计信息。优化
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 10.162.42.51 netmask 255.255.248.0 broadcast 10.162.47.255 inet6 fe80::6e0b:84ff:fed5:88d1 prefixlen 64 scopeid 0x20<link> ether 6c:0b:84:d5:88:d1 txqueuelen 1000 (Ethernet) RX packets 2953454 bytes 414212810 (395.0 MiB) RX errors 0 dropped 4636605 overruns 0 frame 0 TX packets 127887 bytes 82943405 (79.1 MiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Linux 内核提供了 /proc 伪文件系统,经过/proc能够查看内核内部数据结构、改变内核设置。咱们先跑一下题,看一下这个伪文件系统里都有啥:ui
/proc/sys
目录能够查看或修改内核参数/proc/cpuinfo
能够查看CPU信息/proc/meminfo
能够查看内存信息/proc/interrupts
统计全部的硬中断/proc/softirqs
统计的全部的软中断信息/proc/slabinfo
统计了内核数据结构的slab内存使用状况/proc/net/dev
能够看到一些网卡统计数据详细聊下伪文件/proc/net/dev
,经过它能够看到内核中对网卡的一些相关统计。包含了如下信息:.net
因此,伪文件/proc/net/dev
也能够做为咱们查看网卡工做统计数据的工具之一。
sysfs和/proc相似,也是一个伪文件系统,可是比proc更新,结构更清晰。 其中的/sys/class/net/eth0/statistics/
也包含了网卡的统计信息。
# cd /sys/class/net/eth0/statistics/ # grep . * | grep tx tx_aborted_errors:0 tx_bytes:170699510 tx_carrier_errors:0 tx_compressed:0 tx_dropped:0 tx_errors:0 tx_fifo_errors:0 tx_heartbeat_errors:0 tx_packets:262330 tx_window_errors:0
好了,简单了解过这几个工具之后,让咱们正式开始今天的行程。
前面咱们看到,当网线中的数据帧到达网卡后,第一站就是RingBuffer(网卡经过DMA机制将数据帧送到RingBuffer中)。所以咱们第一个要监控和调优的就是网卡的RingBuffer,咱们使用ethtool
来来查看一下Ringbuffer。
# ethtool -g eth0 Ring parameters for eth0: Pre-set maximums: RX: 4096 RX Mini: 0 RX Jumbo: 0 TX: 4096 Current hardware settings: RX: 512 RX Mini: 0 RX Jumbo: 0 TX: 512
这里看到我手头的网卡设置RingBuffer最大容许设置到4096,目前的实际设置是512。
这里有一个小细节,ethtool查看到的是实际是Rx bd的大小。Rx bd位于网卡中,至关于一个指针。RingBuffer在内存中,Rx bd指向RingBuffer。Rx bd和RingBuffer中的元素是一一对应的关系。在网卡启动的时候,内核会为网卡的Rx bd在内存中分配RingBuffer,并设置好对应关系。
在Linux的整个网络栈中,RingBuffer起到一个任务的收发中转站的角色。对于接收过程来说,网卡负责往RingBuffer中写入收到的数据帧,ksoftirqd内核线程负责从中取走处理。只要ksoftirqd线程工做的足够快,RingBuffer这个中转站就不会出现问题。可是咱们设想一下,假如某一时刻,瞬间来了特别多的包,而ksoftirqd处理不过来了,会发生什么?这时RingBuffer可能瞬间就被填满了,后面再来的包网卡直接就会丢弃,不作任何处理!
那咱们怎么样能看一下,咱们的服务器上是否有由于这个缘由致使的丢包呢? 前面咱们介绍的四个工具均可以查看这个丢包统计,拿ethtool
来举例:
# ethtool -S eth0 ...... rx_fifo_errors: 0 tx_fifo_errors: 0
rx_fifo_errors若是不为0的话(在 ifconfig 中体现为 overruns 指标增加),就表示有包由于RingBuffer装不下而被丢弃了。那么怎么解决这个问题呢?很天然首先咱们想到的是,加大RingBuffer这个“中转仓库”的大小。经过ethtool就能够修改。
# ethtool -G eth1 rx 4096 tx 4096
这样网卡会被分配更大一点的”中转站“,能够解决偶发的瞬时的丢包。不过这种方法有个小反作用,那就是排队的包过多会增长处理网络包的延时。因此另一种解决思考更好,。那就是让内核处理网络包的速度更快一些,而不是让网络包傻傻地在RingBuffer中排队。怎么加快内核消费RingBuffer中任务的速度呢,别着急,咱们继续往下看...
在数据被接收到RingBuffer以后,下一个执行就是就是硬中断的发起。咱们先来查看硬中断,而后再聊下怎么优化。
硬中断的状况能够经过内核提供的伪文件/proc/interrupts
来进行查看。
$ cat /proc/interrupts CPU0 CPU1 CPU2 CPU3 0: 34 0 0 0 IO-APIC-edge timer ...... 27: 351 0 0 1109986815 PCI-MSI-edge virtio1-input.0 28: 2571 0 0 0 PCI-MSI-edge virtio1-output.0 29: 0 0 0 0 PCI-MSI-edge virtio2-config 30: 4233459 1986139461 244872 474097 PCI-MSI-edge virtio2-input.0 31: 3 0 2 0 PCI-MSI-edge virtio2-output.0
上述结果是我手头的一台虚机的输出结果。上面包含了很是丰富的信息,让咱们一一道来:
virtio1-input.0
的中断号是27这里有两个细节咱们须要关注一下。
1)为何输入队列的中断都在CPU3上呢?
这是由于内核的一个配置,在伪文件系统中能够查看到。
#cat /proc/irq/27/smp_affinity 8
smp_affinity
里是CPU的亲和性的绑定,8是二进制的1000,第4位为1,表明的就是第4个CPU核心-CPU3。
2)对于收包来过程来说,硬中断的总次数表示的是Linux收包总数吗?
不是,硬件中断次数不表明总的网络包数。第一网卡能够设置中断合并,多个网络帧能够只发起一次中断。第二NAPI 运行的时候会关闭硬中断,经过poll来收包。
如今的主流网卡基本上都是支持多队列的,咱们能够经过将不一样的队列分给不一样的CPU核心来处理,从而加快Linux内核处理网络包的速度。这是最为有用的一个优化手段。
每个队列都有一个中断号,能够独立向某个CPU核心发起硬中断请求,让CPU来poll
包。经过将接收进来的包被放到不一样的内存队列里,多个CPU就能够同时分别向不一样的队列发起消费了。这个特性叫作RSS(Receive Side Scaling,接收端扩展)。经过ethtool
工具能够查看网卡的队列状况。
# ethtool -l eth0 Channel parameters for eth0: Pre-set maximums: RX: 0 TX: 0 Other: 1 Combined: 63 Current hardware settings: RX: 0 TX: 0 Other: 1 Combined: 8
上述结果表示当前网卡支持的最大队列数是63,当前开启的队列数是8。对于这个配置来说,最多同时能够有8个核心来参与网络收包。若是你想提升内核收包的能力,直接简单加大队列数就能够了,这比加大RingBuffer更为有用。由于加大RingBuffer只是给个更大的空间让网络帧能继续排队,而加大队列数则能让包更早地被内核处理。ethtool
修改队列数量方法以下:
#ethtool -L eth0 combined 32
咱们前文说过,硬中断发生在哪个核上,它发出的软中断就由哪一个核来处理。全部经过加大网卡队列数,这样硬中断工做、软中断工做都会有更多的核心参与进来。
每个队列都有一个中断号,每个中断号都是绑定在一个特定的CPU上的。若是你不满意某一个中断的CPU绑定,能够经过修改/proc/irq/{中断号}/smp_affinity来实现。
通常处理到这里,网络包的接收就没有大问题了。但若是你有更高的追求,或者是说你并无更多的CPU核心能够参与进来了,那怎么办?放心,咱们也还有方法提升单核的处理网络包的接收速度。
先来说一个实际中的例子,假如你是一位开发同窗,和你对口的产品经理一天有10个小需求须要让你帮忙来处理。她对你有两种中断方式:
咱们如今不考虑及时性,只考虑你的工做总体效率,你以为那种方案下你的工做效率会高呢?或者换句话说,你更喜欢哪种工做状态呢?很明显,只要你是一个正常的开发,都会以为第二种方案更好。对人脑来说,频繁的中断会打乱你的计划,你脑子里刚才刚想到一半技术方案可能也就废了。当产品经理走了之后,你再想捡起来刚被中断之的工做的时候,极可能得花点时间回忆一下子才能继续工做。
对于CPU来说也是同样,CPU要作一件新的事情以前,要加载该进程的地址空间,load进程代码,读取进程数据,各级别cache要慢慢热身。所以若是能适当下降中断的频率,多攒几个包一块儿发出中断,对提高CPU的工做效率是有帮助的。因此,网卡容许咱们对硬中断进行合并。
如今咱们来看一下网卡的硬中断合并配置。
# ethtool -c eth0 Coalesce parameters for eth0: Adaptive RX: off TX: off ...... rx-usecs: 1 rx-frames: 0 rx-usecs-irq: 0 rx-frames-irq: 0 ......
咱们来讲一下上述结果的大体含义
若是你想好了修改其中的某一个参数了的话,直接使用ethtool -C
就能够,例如:
ethtool -C eth0 adaptive-rx on
不过须要注意的是,减小中断数量虽然能使得Linux总体吞吐更高,不过一些包的延迟也会增大,因此用的时候得适当注意。
在硬中断以后,再接下来的处理过程就是ksoftirqd内核线程中处理的软中断了。以前咱们说过,软中断和它对应的硬中断是在同一个核心上处理的。 所以,前面硬中断分散到多核上处理的时候,软中断的优化其实也就跟着作了,也会被多核处理。不过软中断也还有本身的可优化选项。
软中断的信息能够从 /proc/softirqs 读取:
$ cat /proc/softirqs CPU0 CPU1 CPU2 CPU3 HI: 0 2 2 0 TIMER: 704301348 1013086839 831487473 2202821058 NET_TX: 33628 31329 32891 105243 NET_RX: 418082154 2418421545 429443219 1504510793 BLOCK: 37 0 0 25728280 BLOCK_IOPOLL: 0 0 0 0 TASKLET: 271783 273780 276790 341003 SCHED: 1544746947 1374552718 1287098690 2221303707 HRTIMER: 0 0 0 0 RCU: 3200539884 3336543147 3228730912 3584743459
不知道你有没有据说过番茄工做法,它的大体意思就是你要有一整段的不被打扰的时间,集中精力处理某一项做业。这一整段时间时长被建议是25分钟。
对于咱们的Linux的处理软中断的ksoftirqd来讲,它也和番茄工做法思路相似。一旦它被硬中断触发开始了工做,它会集中精力处理一波儿网络包(毫不只是1个),而后再去作别的事情。
咱们说的处理一波儿是多少呢,策略略复杂。咱们只说其中一个比较容易理解的,那就是net.core.netdev_budget
内核参数。
#sysctl -a | grep net.core.netdev_budget = 300
这个的意思说的是,ksoftirqd一次最多处理300个包,处理够了就会把CPU主动让出来,以便Linux上其它的任务能够获得处理。 那么假如说,咱们如今就是想提升内核处理网络包的效率。那就可让ksoftirqd进程多干一下子网络包的接收,再让出CPU。至于怎么提升,直接修改不这个参数的值就行了。
#sysctl -w net.core.netdev_budget=600
若是要保证重启仍然生效,须要将这个配置写到/etc/sysctl.conf
GRO和硬中断合并的思想很相似,不过阶段不一样。硬中断合并是在中断发起以前,而GRO已经到了软中断上下文中了。
若是应用中是大文件的传输,大部分包都是一段数据,不用GRO的话,会每次都将一个小包传送到协议栈(IP接收函数、TCP接收)函数中进行处理。开启GRO的话,Linux就会智能进行包的合并,以后将一个大包传给协议处理函数。这样CPU的效率也是就提升了。
ethtool -k eth0 | grep generic-receive-offload generic-receive-offload: on
若是你的网卡驱动没有打开GRO的话,能够经过以下方式打开。
# ethtool -K eth0 gro on
GRO说的仅仅只是包的接收阶段的优化方式,对于发送来讲是GSO。
在网络技术这一领域里,有太多的知识内容都停留在理论阶段了。你可能以为你的网络学的倒背如流了,但是当你的线上服务出现问题的时候,你仍是不知道该怎么排查,怎么优化。这就是由于只懂了理论,而不清楚Linux是经过哪些内核机制将网络技术落地的,各个内核组件之间怎么配合,每一个组件有哪些参数能够作调整。咱们用两篇文章详细讨论了Linux网络包的接收过程,以及这个过程当中的一些统计数据如何查看,如何调优。相信消化完这两篇文章以后,你的网络的理解直接能提高1个Level,你对线上服务的把控能力也会更加如鱼得水。
开发内功修炼之硬盘篇专辑:
个人公众号是「开发内功修炼」,在这里我不是单纯介绍技术理论,也不仅介绍实践经验。而是把理论与实践结合起来,用实践加深对理论的理解、用理论提升你的技术实践能力。欢迎你来关注个人公众号,也请分享给你的好友~~~