上一篇文章《图解Linux网络包接收过程》,我们梳理了Linux系统下接收一个数据包的整个过程。Linux内核接收网络包的过程大致可以分为几个过程:接收RingBuffer、硬中断处理、ksoftirqd软中断处理。其中,在ksoftirqd软中断处理中,数据包从RingBuffer中取出,送往协议栈处理,然后送到用户进程socket的接收队列。了解了Linux的工作原理之后,还有两件比较重要的事情。第一个是动手监控,其实就是看网络包接收的整体情况。二是调音。当你的服务器出现问题时,你可以通过内核的开放参数找到瓶颈并进行调整。先说几个工具吧。在正式内容开始之前,我们先来了解一下Linux下可以用来监控网卡的几个工具。ethtool第一个工具就是我们上面提到的ethtool,用来查看和设置网卡参数。其实这个工具本身只是提供了几个常用的接口,真正的实现都在网卡驱动中。只是因为这个工具是驱动直接实现的,个人认为是最重要的。这个命令比较复杂。让我们选择一些今天可以使用的。-i显示网卡驱动信息,如驱动名称、版本等。-S查看网卡收发包的统计信息。-l/-L查看或修改网卡队列数-c/-C查看或修改硬中断合并策略实际查看网卡驱动:#ethtool-ieth0driver:ixgbe...查看我机器网卡这里的驱动是ixgbe。有了驱动名称,就可以在源码中找到对应的代码了。对于ixgbe,其驱动的源代码位于drivers/net/ethernet/intel/ixgbe目录下。ixgbe_ethtool.c下是ethtool实现的相关函数。如果ethtool有不明白的地方,可以通过这种方式找到源码阅读。另外,上篇文章《图解Linux网络包接收过程》中提到的NAPI收包时的poll回调函数,启动网卡时的open函数,都是在这里实现的。ifconfig网络管理工具ifconfig不仅可以为网卡配置ip,启用或禁用网卡,还包括网卡的一些统计信息。eth0:flags=4163mtu1500inet10.162.42.51netmask255.255.248.0broadcast10.162.47.255inet6fe80::6e0b:84ff:fed5:88d1prefixlen60x20link:6:884:d5:d1txqueuelen1000(以太网)RX数据包2953454字节414212810(395.0MiB)RX错误0丢弃4636605溢出0帧0TX数据包127887字节82943405(79.1MiB)TX错误0丢弃0溢出0载波0冲突0RX数据包:总数ofpacketsreceivedRXbytes:接收到的字节数RXerrors:表示接收到的包错误总数RXdropped:数据包已经进入RingBuffer,但由于其他原因丢包fifo是由于RingBuffer不足导致丢包造成的。伪文件系统/procLinux内核提供了/proc伪文件系统。通过/proc,可以查看内核内部数据结构,更改内核设置。我们先跑题看看这个伪文件系统里有什么:/proc/sys目录可以查看或修改内核参数/proc/cpuinfo可以查看CPU信息/proc/meminfo可以查看内存信息/proc/interrupts统计所有硬中断/proc/softirqs统计所有软中断信息/proc/slabinfo统计内核数据结构的slab内存使用情况/proc/net/dev可以看到一些网卡统计,详细说一下伪文件/proc/net/dev,通过它可以看到内核中与网卡相关的一些统计信息。包含以下信息:bytes:发送或接收数据的总字节数packets:接口发送或接收的数据包总数errs:设备驱动检测到的发送或接收错误总数drop:被接口丢弃的数据设备驱动Totalnumberofpacketsfifo:FIFO缓冲区错误数frame:数据包成帧错误数。(数据包帧错误数)colls:接口上检测到的冲突数所以,伪文件/proc/net/dev也可以作为我们查看网卡工作统计的工具之一。伪文件系统sysfssysfs与/proc类似,也是伪文件系统,但比proc更新,结构更清晰。/sys/class/net/eth0/statistics/也包含了网卡的统计信息。#cd/sys/class/net/eth0/statistics/#grep。*|GREPTXTX_ABORTED_ERRORS:0TX_BYTES:170699510TX_CARRIER_ERRORS:0TX_COMPREDSE:0TX_DROPPEDSE:0TX_DROPPED:0TX_FIFO_ERRORS:0TX_FIFO_ERRORS:0TX_HEARTBEATBEATBERTINGofferentofferityofertiantiantiantiantir:0tx_packets:262330tx330330tx330tx330tx330TX330TX330TX330TX330,RingBuffer监控与调优我们前面看到,当网线中的数据帧到达网卡时,第一站是RingBuffer(网卡通过DMA机制将数据帧发送到RingBuffer)。因此,我们首先需要监控和调优的就是网卡的RingBuffer。我们使用ethtool来检查Ringbuffer。#ethtool-geth0Ringparametersforeth0:Pre-setmaximums:RX:4096RXMini:0RXJumbo:0TX:4096Currenthardwaresettings:RX:512RXMini:0RXJumbo:0TX:512在这里你可以看到我的最大允许RingBuffer设置网卡设置为4096,当前实际设置为512。这里有个小细节,ethtool看到的是Rxbd的实际大小。Rxbd位于网卡中,相当于一个指针。RingBuffer在内存中,Rxbd指向RingBuffer。Rxbd和RingBuffer中的元素是一一对应的。网卡启动时,内核会在内存中为网卡的Rxbd分配RingBuffer,并建立对应关系。在Linux的整个网络栈中,RingBuffer扮演着任务收发中转站的角色。对于接收过程,网卡负责将接收到的数据帧写入RingBuffer,ksoftirqd内核线程负责取出来处理。只要ksoftirqd线程工作得够快,RingBuffer中转站就不会有问题。但是我们试想一下,如果在某个时刻,瞬间来了很多包,但是ksoftirqd处理不了,会怎样呢?这时候RingBuffer可能会瞬间被填满,后面的网卡不做任何处理直接丢弃!那么如何查看我们的服务器是否因为这个原因丢包呢?上面我们介绍的四种工具都可以查看丢包统计。以ethtool为例:#ethtool-Seth0......rx_fifo_errors:0tx_fifo_errors:如果0rx_fifo_errors不为0(在ifconfig中体现为overrunsindicatorgrowth),说明一个包被丢弃,因为RingBuffer放不下。那么如何解决这个问题呢?自然而然,我们首先想到的就是增加RingBuffer这个“中转仓库”的大小。可以通过ethtool修改。#ethtool-Geth1rx4096tx4096这样网卡就会分配一个更大的“中转站”,可以解决偶尔出现的瞬时丢包。但是,这种方法有一个小的副作用,就是队列中的数据包过多会增加处理网络数据包的延迟。所以另一种解决思路更好。那就是让内核更快地处理网络包,而不是让网络包傻傻地在RingBuffer中排队。如何加快内核对RingBuffer中任务的消耗?不着急,我们继续往下看……硬中断监控与调优。RingBuffer接收到数据后,接下来的执行就是发起硬中断。先看硬中断,再说怎么优化。硬中断的监控可以通过内核提供的伪文件/proc/interrupts查看。$cat/proc/interruptsCPU0CPU1CPU2CPU30:34000IO-APIC-edgetimer......27:351001109986815PCI-MSI-edgevirtio1-input.028:2571000PCI-MSI-edgevirtio1-output.029:0000PCI-MSI-edgevirtio2-config30:42334591986139461244872474097PCI-MSI-edgevirtio2-input.031:3020PCI-MSI-edgevirtio2-output.0上面的结果是我手边一台虚拟机的输出。上面包含了很多信息,我们一一梳理:网卡输入队列virtio1-input.0的中断号为2727,总中断数为1109986815,均由CPU3处理。这里有两个细节需要我们注意。1)为什么输入队列的中断都在CPU3上?这是因为内核的配置,可以在伪文件系统中查看。#cat/proc/irq/27/smp_affinity8smp_affinity是CPUaffinity的绑定,8是二进制1000,第四位是1,代表第四个CPU核心——CPU3。2)对于接收包的处理,硬中断的总数是否代表Linux接收到的包总数?不,硬件中断的数量并不代表网络数据包的总数。第一个网卡可以设置组合中断,多个网卡只能发起一个中断。当第二个NAPI运行时,会关闭硬中断,通过poll方式接收数据包。多队列网卡调优目前主流的网卡基本都支持多队列。我们可以将不同的队列分配给不同的CPU核心进行处理,从而加快Linux内核处理网络数据包的速度。这是最有用的优化方法。每个队列都有一个中断号,可以独立向某个CPU核发起硬中断请求,让CPU轮询包。通过将接收到的数据包放入不同的内存队列中,多个CPU可以同时向不同的队列发起消费。这个特性叫做RSS(ReceiveSideScaling,接收端缩放)。可以通过ethtool工具查看网卡的队列状态。#ethtool-leth0Channelparametersforeth0:Pre-setmaximums:RX:0TX:0Other:1Combined:63Currenthardwaresettings:RX:0TX:0Other:1Combined:8以上结果表示当前网络支持的最大队列数card为63,当前启用队列数为8。对于这个配置,最多可以有8个core同时参与网络数据包采集。如果想提高内核接收数据包的能力,只需增加队列的数量,这比增加RingBuffer更有用。因为增加RingBuffer只是给网络帧提供了更多的空间继续排队,而增加队列的数量可以让数据包更早地被内核处理。ethtool修改队列数的方法如下:#ethtool-Leth0combined32我们之前说过,硬中断发生在哪个核上,它发出的软中断就会被那个核处理。都是通过增加网卡队列的数量,让更多的核参与硬中断工作和软中断工作。每个队列都有一个中断号,每个中断号都绑定到一个特定的CPU。如果对某个中断的CPU绑定不满意,可以通过修改/proc/irq/{interruptnumber}/smp_affinity来实现。一般到这里处理后,接收网络包就没有大问题了。但是如果你有更高的追求,或者没有更多的CPU核心可以参与怎么办?别担心,我们也有办法提高单核处理网络数据包的接收速度。硬中断合并先说一个实际的例子。假设你是一名开发同学,你的对口产品经理每天有10个小需求需要你帮忙处理。她打断你的方式有两种:第一种:产品经理想到一个需求就来找你,给你描述需求的细节,然后让你帮你改;第二个:产品经理一想到需求就懒得理你,你存到5就来找你,我们现在不考虑你集中处理的时效性,只考虑整体效率你的工作。你觉得在那个方案下你的工作效率会高吗?或者换句话说,你更喜欢哪种工作状态?显然,只要你是一个普通的开发者,你都会觉得第二种方案更好。就人脑而言,频繁的打扰会打乱你的计划,脑子里刚刚想到的技术方案有一半可能都没用。当产品经理不在的时候,当你想接起刚刚被打断的工作时,你可能要花一些时间回忆一下,然后才能继续工作。CPU也是如此。CPU在做一件新的事情之前,需要加载进程的地址空间,加载进程代码,读取进程数据,慢慢预热每一级缓存。因此,如果能适当降低中断的频率,多节省几个包一起发送中断,将有助于提高CPU的工作效率。因此,网卡允许我们合并硬中断。下面我们看一下网卡的硬中断合并配置。#ethtool-ceth0Coalesceparametersforeth0:AdaptiveRX:offTX:off...rx-usecs:1rx-frames:0rx-usecs-irq:0rx-frames-irq:0...us说一下大概意思以上结果AdaptiveRX:自适应中断合并,网卡驱动判断什么时候合并,什么时候不合并,将产生RX中断。如果要修改其中一个参数,可以直接使用ethtool-C,例如:ethtool-Ceth0adaptive-rxon但需要注意的是,虽然减少中断次数可以让Linux的整体吞吐量更高,但是有些包的延时也会增加,使用的时候一定要注意。软中断监控和调优在硬中断之后,接下来的处理过程就是ksoftirqd内核线程中处理的软中断。我们之前说过,软中断和对应的硬中断是在同一个核上处理的。Therefore,whentheprevioushardinterruptsweredistributedtomultiplecoresforprocessing,theoptimizationofsoftinterruptswasactuallydoneaccordingly,andtheywouldalsobeprocessedbymultiplecores.However,softirqsalsohavetheirownoptimizationoptions.监控软中断的信息可以从/proc/softirqs读取:$cat/proc/softirqsCPU0CPU1CPU2CPU3HI:0220TIMER:70430134810130868398314874732202821058NET_TX:336283132932891105243NET_RX:41808215424184215454294432191504510793BLOCK:370025728280BLOCK_IOPOLL:0000TASKLET:271783273780276790341003SCHED:1544746947137455271812870986902221303707HRTIMER:0000RCU:3200539884333654314732287309123584743459软中断budget调整不知道你有没有听说过番茄工作法,它Thegeneralmeaningisthatyouhavetohaveawholeperiodofuninterruptedtimetoconcentrateonacertainjob.Thedurationofthisentireperiodissuggestedtobe25minutes.ForourLinuxksoftirqdthathandlessoftinterrupts,itisalsosimilartothePomodoroTechnique.Onceitistriggeredbyahardinterrupttostartworking,itwillfocusonprocessingawaveofnetworkpackets(notjust1),andthengotootherthings.Howmuchdowedealwithawave?Thestrategyisalittlecomplicated.就说其中一个比较容易理解的,就是net.core.netdev_budget内核参数。#sysctl-a|grepnet.core.netdev_budget=300这意味着ksoftirqd一次最多可以处理300个包,如果处理够了,它会主动让出CPU,让Linux上的其他任务处理。所以如果说,我们只是想提高内核处理网络数据包的效率。那么可以让ksoftirqd进程多接收一会网络包,然后让出CPU。至于如何改进,直接修改这个参数的值即可。#sysctl-wnet.core.netdev_budget=600如果要保证重启仍然生效,需要把这个配置写到/etc/sysctl.conf软中断GRO合并GRO和硬中断合并的思路很相似,只是阶段不同。硬中断合并是在中断发起之前,GRO已经到达软中断上下文。如果应用是传输大文件,大部分的数据包都是一段数据。如果不使用GRO,每次都会发送一个小包到协议栈(IP接收函数,TCP接收)函数中处理。如果启用GRO,Linux会智能合并包,然后将一个大包传递给协议处理函数。这样一来,CPU的效率也得到了提升。ethtool-keth0|grepgeneric-receive-offloadgeneric-receive-offload:on如果你的网卡驱动没有开启GRO,可以通过以下方式开启。#GRO上的ethtool-Keth0gro只是讲了数据包接收阶段的优化方法,发送的是GSO。综上所述,在网络技术领域,太多的知识内容停留在理论阶段。您可能认为自己对网络学习很熟悉,但是当您的在线服务出现问题时,您仍然不知道如何排查,如何优化。这是因为我只了解理论,却不知道Linux采用何种内核机制来实现网络技术,各个内核组件之间是如何相互配合的,各个组件可以调整哪些参数。我们用两篇文章详细讨论了Linux网络数据包的接收过程,以及如何查看和调优这个过程中的一些统计信息。相信消化完这两篇文章后,你对互联网的理解可以直接提升1个Level,驾驭在线服务的能力也会更加得心应手。开发内功实训硬盘相册:图解Linux网络包接收过程Linux网络包接收过程监控与调优浅谈TCP连接耗时的那些事儿介绍技术理论,而不仅仅是实践经验。而是理论联系实际,用实践加深对理论的理解,用理论提高技术实践能力。欢迎关注我的,分享给你的朋友吧~~~