在分析服务器的网络子系统的操作时,通常会注意诸如延迟,系统吞吐量和每秒可处理的数据包数量(PPS,每秒数据包)等指标。使用这些指标是为了了解所研究的计算机可以在最大负载下工作。而且,尽管这些指标很重要,并且通常可以说出很多关于系统的信息,但它们并未提供有关网络数据包处理对服务器上运行的程序有何影响的信息。
该材料旨在研究服务器上网络机制所产生的负载。特别是,我们将讨论网络问题解决方案可以从Linux系统上运行的各种进程“窃取”多少处理器时间。
Linux上的网络数据包处理
在处理相应的IRQ时,Linux在处理器执行的任何处理的上下文中处理大量数据包。系统记帐引擎会将用于此目的的处理器周期分配给当前正在执行的任何进程。即使此过程与网络数据包处理无关,也将执行此操作。例如,一条命令top
可能表明一个进程似乎正在使用超过99%的处理器资源,但实际上60%的处理器时间将花费在处理数据包上。这意味着进程本身解决了自己的问题,仅使用40%的CPU资源。入站处理程序net_rx_action
通常执行得非常非常快。例如,小于25μs。 (此数据来自使用eBPF进行的测量。如果您对详细信息感兴趣,请单击此处 net_rx_action
。)在将任务推迟到另一个SoftIRQ周期之前,处理器每个NAPI实例(NIC或RPS)最多可以处理64个数据包。可以不间断地接连进行多达10个SoftIRQ周期,这大约需要2毫秒(您可以通过阅读about来了解更多信息__do_softirq
)。如果在经过最大周期数或时间之后,SoftIRQ向量仍然存在未解决的问题,则这些问题的解决方案将延迟在线程中执行ksoftirqd
特定的CPU。发生这种情况时,从获取有关网络操作产生的处理器负载的信息的意义上讲,该系统变得更加透明(尽管这种分析是在假设研究的是SoftIRQ的情况下进行的,它与数据包处理有关,与其他无关)。 。获得上述指标的一种方法是使用perf
:sudo perf record -a \
-e irq:irq_handler_entry,irq:irq_handler_exit
-e irq:softirq_entry --filter="vec == 3" \
-e irq:softirq_exit --filter="vec == 3" \
-e napi:napi_poll \
-- sleep 1
sudo perf script
结果如下:swapper 0 [005] 176146.491879: irq:irq_handler_entry: irq=152 name=mlx5_comp2@pci:0000:d8:00.0
swapper 0 [005] 176146.491880: irq:irq_handler_exit: irq=152 ret=handled
swapper 0 [005] 176146.491880: irq:softirq_entry: vec=3 [action=NET_RX]
swapper 0 [005] 176146.491942: napi:napi_poll: napi poll on napi struct 0xffff9d3d53863e88 for device eth0 work 64 budget 64
swapper 0 [005] 176146.491943: irq:softirq_exit: vec=3 [action=NET_RX]
swapper 0 [005] 176146.491943: irq:softirq_entry: vec=3 [action=NET_RX]
swapper 0 [005] 176146.491971: napi:napi_poll: napi poll on napi struct 0xffff9d3d53863e88 for device eth0 work 27 budget 64
swapper 0 [005] 176146.491971: irq:softirq_exit: vec=3 [action=NET_RX]
swapper 0 [005] 176146.492200: irq:irq_handler_entry: irq=152 name=mlx5_comp2@pci:0000:d8:00.0
在这种情况下,处理器处于空闲状态(因此会出现swapper
该进程的条目),在CPU 5上为Rx队列调用IRQ,两次调用SoftIRQ处理,首先处理64个数据包,然后处理27个。在229μs之后调用下一个IRQ,然后重新开始循环。该数据是在空闲系统上获得的。但是在处理器上,可以执行任何任务。在这种情况下,将发生上述事件序列,从而中断此任务并执行IRQ / SoftIRQ任务。同时,系统计费将中断进程归因于处理器所创建的负载。结果,通常对常规处理器负载监视工具隐藏网络分组处理任务。它们是在“受害者程序”的背景下,在一些随机选择的过程中执行的。这使我们提出了一些问题。如何估计处理数据包过程中断的时间?如何比较两种不同的网络解决方案,以了解它们对计算机上解决的各种任务的影响较小?当使用RSS,RPS,RFS机制时,数据包处理通常分布在处理器内核之间。因此,上述分组处理序列与每个特定CPU有关。随着数据包到达率的提高(我认为我们可以谈论每秒100,000个数据包以及更高的速度),每个CPU每秒必须处理数千或数万个数据包。处理如此多的数据包将不可避免地影响服务器上执行的其他任务。考虑一种评估这种效果的方法。禁用分布式数据包处理
首先,让我们通过禁用RPS并设置流控制规则来停止数据包的分布式处理,该规则旨在组织对我们已知的唯一CPU上与特定MAC地址有关的所有数据包的处理。我的系统在802.3ad配置中聚合了2个NIC。网络任务被分配给计算机上运行的单个虚拟机。网络适配器上的RPS禁用如下:for d in eth0 eth1; do
find /sys/class/net/${d}/queues -name rps_cpus |
while read f; do
echo 0 | sudo tee ${f}
done
done
接下来,我们设置流控制规则以确保数据包进入使用单个CPU的测试虚拟机:DMAC=12:34:de:ad:ca:fe
sudo ethtool -N eth0 flow-type ether dst ${DMAC} action 2
sudo ethtool -N eth1 flow-type ether dst ${DMAC} action 2
禁用RPS并使用流控制规则使我们能够确保所有发往虚拟机的数据包都在同一CPU上处理。为了确保将数据包发送到应将其发送到的队列,可以使用类似ethq的命令。然后,您可以使用找出此队列属于哪个CPU /proc/interrupts
。在我的情况下,通过CPU 5处理转弯2。OpenSSL速度命令
我可以使用实用程序perf
或分析负责处理传入流量的SoftIRQ运行时bpf
,但是这种方法非常复杂。另外,观察过程本身肯定会影响结果。一种更简单,更易理解的解决方案是使用某些任务来识别由网络操作对系统造成的负载,该任务会在系统上造成已知负载。例如,这是openssl speed
用于测试OpenSSL性能的命令。这将使您找出程序实际获得了多少处理器资源,并将其与应该接收的资源量进行比较(这将有助于找出在网络任务上花费了多少资源)。该团队openssl speed
几乎是100%的用户空间团队。如果将其绑定到某个CPU,则在执行测试期间,它将使用其所有可用资源。该团队的工作方式是将计时器设置为指定的时间间隔(例如,为了使计算更容易,需要10秒),运行测试,然后在触发计时器时使用times
()
它来找出程序实际获得了多少处理器时间。从角度来看,syscall
它看起来像这样:alarm(10) = 0
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 1726601344
--- SIGALRM {si_signo=SIGALRM, si_code=SI_KERNEL} ---
rt_sigaction(SIGALRM, ...) = 0
rt_sigreturn({mask=[]}) = 2782545353
times({tms_utime=1000, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 1726602344
也就是说,事实证明,在调用alarm()
和检查结果之间很少进行系统调用。如果程序没有被中断,或者中断很少,那么时间tms_utime
将与测试时间一致(在这种情况下为10秒)。由于这是一项仅在用户空间中执行的测试,因此出现的任何系统时间times()
都将对系统造成额外的负担。事实证明,尽管openssl
这是一个在CPU上运行的进程,但CPU本身可能正忙于其他事情。例如,处理网络数据包:alarm(10) = 0
times({tms_utime=0, tms_stime=0, tms_cutime=0, tms_cstime=0}) = 1726617896
--- SIGALRM {si_signo=SIGALRM, si_code=SI_KERNEL} ---
rt_sigaction(SIGALRM, ...) = 0
rt_sigreturn({mask=[]}) = 4079301579
times({tms_utime=178, tms_stime=571, tms_cutime=0, tms_cstime=0}) = 1726618896
在这里您可以看到openssl
可以在处理器上工作7.49秒(178 + 571,以0.01 s为单位的测量单位)。但同时5.71秒。此间隔由系统时间表示。由于他openssl
不忙于内核空间的任何事务,因此这意味着5.71秒。-这是系统上一些额外负载的结果。也就是说,这是为了满足系统需求而“窃取”进程的时间。使用openssl speed命令检测由网络机制引起的系统负载
现在我们已经弄清了团队的工作方式openssl speed
,我们将看看它在几乎不活动的服务器上产生的结果:$ taskset -c 5 openssl speed -seconds 10 aes-256-cbc >/dev/null
Doing aes-256 cbc for 10s on 16 size blocks: 66675623 aes-256 cbc's in 9.99s
Doing aes-256 cbc for 10s on 64 size blocks: 18096647 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 256 size blocks: 4607752 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 1024 size blocks: 1162429 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 8192 size blocks: 145251 aes-256 cbc's in 10.00s
Doing aes-256 cbc for 10s on 16384 size blocks: 72831 aes-256 cbc's in 10.00s
如您所见,我们被告知该程序花费9.99到10秒来处理不同大小的块。这证实了系统机制不会占用程序的处理器时间。现在,使用netperf
,我们将通过处理来自两个来源的数据包来加载服务器。再次运行测试:$ taskset -c 5 openssl speed -seconds 10 aes-256-cbc >/dev/null
Doing aes-256 cbc for 10s on 16 size blocks: 12061658 aes-256 cbc's in 1.96s
Doing aes-256 cbc for 10s on 64 size blocks: 3457491 aes-256 cbc's in 2.10s
Doing aes-256 cbc for 10s on 256 size blocks: 893939 aes-256 cbc's in 2.01s
Doing aes-256 cbc for 10s on 1024 size blocks: 201756 aes-256 cbc's in 1.86s
Doing aes-256 cbc for 10s on 8192 size blocks: 25117 aes-256 cbc's in 1.78s
Doing aes-256 cbc for 10s on 16384 size blocks: 13859 aes-256 cbc's in 1.89s
结果与在空闲服务器上获得的结果非常不同。预计每个测试将在10秒内执行,但是times()
报告实际执行时间为1.78到2.1秒。这意味着在过程的上下文中openssl
或中,剩余的时间(从7.9到8.22秒不等)花费在处理数据包上ksoftirqd
。让我们看一下团队top
在分析刚刚完成的发布时会给出什么openssl speed
。PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND P
8180 libvirt+ 20 0 33.269g 1.649g 1.565g S 279.9 0.9 18:57.81 qemu-system-x86 75
8374 root 20 0 0 0 0 R 99.4 0.0 2:57.97 vhost-8180 89
1684 dahern 20 0 17112 4400 3892 R 73.6 0.0 0:09.91 openssl 5
38 root 20 0 0 0 0 R 26.2 0.0 0:31.86 ksoftirqd/5 5
在这里,您可能会认为它openssl
占用了CPU 5大约73%的资源,而ksoftirqd
剩余的资源已获得。但是实际上,在上下文中openssl
,执行的程序包数量如此之多,以至于程序本身仅占用处理器时间的18-21%即可解决其问题。如果将网络负载减少到1个流,您openssl
会感觉到99%的系统资源已被消耗。 PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND P
8180 libvirt+ 20 0 33.269g 1.722g 1.637g S 325.1 0.9 166:38.12 qemu-system-x86 29
44218 dahern 20 0 17112 4488 3996 R 99.2 0.0 0:28.55 openssl 5
8374 root 20 0 0 0 0 R 64.7 0.0 60:40.50 vhost-8180 55
38 root 20 0 0 0 0 S 1.0 0.0 4:51.98 ksoftirqd/5 5
但实际上,事实证明,在用户空间中运行的程序在预期的10秒中只有大约4秒:Doing aes-256 cbc for 10s on 16 size blocks: 26596388 aes-256 cbc's in 4.01s
Doing aes-256 cbc for 10s on 64 size blocks: 7137481 aes-256 cbc's in 4.14s
Doing aes-256 cbc for 10s on 256 size blocks: 1844565 aes-256 cbc's in 4.31s
Doing aes-256 cbc for 10s on 1024 size blocks: 472687 aes-256 cbc's in 4.28s
Doing aes-256 cbc for 10s on 8192 size blocks: 59001 aes-256 cbc's in 4.46s
Doing aes-256 cbc for 10s on 16384 size blocks: 28569 aes-256 cbc's in 4.16s
传统的进程监视工具表明该程序几乎使用了所有处理器资源,但实际上,事实证明,有55-80%的CPU资源用于处理网络数据包。同时,系统的吞吐量看起来很高(每25 Gb / s线超过22 Gb / s),但这对在该系统中运行的进程产生了巨大影响。摘要
在这里,我们研究了一个示例,该示例说明了数据包处理机制如何从一个简单但不太重要的基准中“窃取”处理器时钟。但是在真实服务器上,受到类似影响的进程可以是任何东西。这些可以是虚拟机的虚拟处理器,仿真器线程,虚拟主机线程。这些可能是不同的系统过程,对其的影响可能会对这些过程和整个系统的性能产生不同的影响。您是否在分析服务器时考虑了与网络操作相关的负载对其实际性能的影响?