软中断(softirq)CPU使用率升高也是最常见的一种性能问题
从“取外卖”看中断
中断是系统用来响应硬件设备请求的一种机制。它会打断进程的正常调度和执行,然后调用内核中的中断处理程序来响应设备的请求。
为什么要有中断呢?
⽐如说你订了⼀份外卖,但是不确定外卖什么时候送到,也没有别的⽅法了解外卖的进度,但是,配送员送外卖是不等⼈的,
到了你这⼉没⼈取的话,就直接⾛⼈了。所以你只能苦苦等着,时不时去⻔⼝看看外卖送到没,⽽不能⼲其他事情。
不过呢,如果在订外卖的时候,你就跟配送员约定好,让他送到后给你打个电话,那你就不⽤苦苦等待了,就可以去忙别的事情,直到电话⼀响,接电话、取外卖就可以了。
这⾥的“打电话”,其实就是⼀个中断。没接到电话的时候,你可以做其他的事情;只有接到了电话(也就是发⽣中断),你才要进⾏另⼀个动作:取外卖。
中断的作用
从上面这个例子可以看出:中断其实是一种异步机制,可以提高系统的并发处理能力
那么还是以取外卖为例。假如你订了 2 份外卖,⼀份主⻝和⼀份饮料,并且是由2个不同的配送员来配送。这次你不⽤时时等待着,两份外卖都约定了电话取外卖的⽅式。但是,问题⼜来了。
软中断
如果你弄清楚了“取外卖”的模式,那对系统的中断机制就很容易理解了。事实上,为了解决中断处理程序执行过长和中断丢失的问题,Linux将中断处理过程分为了两个阶段:上半步和下半步
- 上半步用来快速处理中断,它在中断机制模式下运行,主要处理跟硬件紧密相关的或时间敏感的工作
- 下半部用来延迟处理上半部未完成的工作,通常以内核线程的方式运行
⽐如说前⾯取外卖的例⼦,上半部就是你接听电话,告诉配送员你已经知道了,其他事⼉⻅⾯再说,然后电话就可以挂断了;下半部才是取外卖的动作,以及⻅⾯后商量发票处理的动作。
这样,第⼀个配送员不会占⽤你太多时间,当第⼆个配送员过来时,照样能正常打通你的电话。
除了取外卖,再举个最常⻅的⽹卡接收数据包的例⼦:
- 网卡接收到数据包之后,会通过硬件中断的方式,通知内核有新的数据到了。这时,内核就应该调用中断处理程序来响应它。
- 对于上半步,既然是快速处理,其实就是要把网卡的数据读到内存中,然后更新一下硬件寄存器的状态(表述数据已经读好了),最后发送一个软中断信号,通知下半部做进一步的处理
- 而下半步被软中断信号唤醒后,需要从内存中找到网络数据,再按照网络协议栈,对数据进行逐层解析和处理,直到把它送给应用程序。
所以,这两个阶段你也可以这样理解:
- 上半步直接处理硬件请求,也就是我们常说的硬中断,特点是快速执行
- 下半步则是由内核触发,也就是我们常说的软中断,特点是延迟执行,用来异步处理上半步未完成的工作
实际上,上半步会打断CPU正在执行的任务,然后立即执行中断处理程序,而下半步以内核线程的方式执行,并且每个CPU都对应一个软中断内核线程,名字为"ksoftirqd/CPU编号",比如说,0号CPU对应的软中断内核线程的名字就是ksoftirqd/0
不过要注意的是,软中断不只包括了硬件设备中断程序的下半步,一些内核自定义的事件也属于软中断,比如内核调度和RCU锁(read-copy update
,RCU是内核中最常用的锁之一)等
那要怎么知道你得系统里有哪些软中断了
proc文件系统,是一种内核空间和用户空间进行通信的机制,可以用来查看内核的数据结构,或者用来动态修改内核的配置。其中:
- /proc/softirqs 提供了软中断的运⾏情况;
- /proc/interrupts 提供了硬中断的运⾏情况。
运⾏下⾯的命令,查看 /proc/softirqs ⽂件的内容,你就可以看到各种类型软中断在不同 CPU 上的累积运⾏次数:
$ cat /proc/softirqs
CPU0 CPU1
HI: 0 0
TIMER: 811613 1972736
NET_TX: 49 7
NET_RX: 1136736 1506885
BLOCK: 0 0
IRQ_POLL: 0 0
TASKLET: 304787 3691
SCHED: 689718 1897539
HRTIMER: 0 0
RCU: 1330771 1354737
在查看 /proc/softirqs ⽂件内容时,你要特别注意以下这两点。
- 第⼀,要注意软中断的类型,也就是这个界⾯中第⼀列的内容。从第⼀列你可以看到,软中断包括了10个级别,分别丢应不同的工作类型。比如NET_RX表示网络接收中断,NET_TX表示网络发送中断
- 第⼆,要注意同⼀种软中断在不同 CPU 上的分布情况,也就是同⼀⾏的内容。正常清空下同一种中断在不同CPU上的累积次数应该差不多。⽐如这个界⾯中,NET_RX 在 CPU0 和 CPU1 上的中断次数基本是同⼀个数量级,相差不⼤。
不过你可能发现,TASKLET 在不同CPU上的分布并不均匀。
- TASKLEN是最常用的软中断实现机制。每个TASKLEN只允许一次就会结束,并且只在调用它的函数所在的CPU上运行。
- 因此,使用TASKLEN特别简便,当然也会存在一些问题,比如由于只在一个CPU上运行导致的调度不均衡,比如因为不能在多个CPU上并行运行带来了性能限制
Linux 中的软中断包括⽹络收发、定时、调度、RCU锁等各种类型,可以通过查看 /proc/softirqs 来观察软中断的运⾏情况。
软中断实际上是以内核线程的方式运行的,每个CPU都对应一个软中断内核线程,这个软中断内核线程就叫做 ksoftirqd/CPU编号。 那要怎么查看这些线程的运⾏状况呢?
其实⽤ ps 命令就可以做到,⽐如执⾏下⾯的指令:
$ ps aux | grep softirq
root 7 0.0 0.0 0 0 ? S Oct10 0:01 [ksoftirqd/0]
root 16 0.0 0.0 0 0 ? S Oct10 0:01 [ksoftirqd/1]
注意,这些线程的名字外⾯都有中括号,这说明ps ⽆法获取它们的命令⾏参数(cmline)。⼀般来说,ps 的输出中,名字括在中括号⾥的,⼀般都是内核线程。
其他
- ⼤量的⽹络⼩包会导致性能问题,是因为量的⼩⽹络包会导致频繁的硬中断和软中断呢?
系统的软中断CPU使用率升高,我该怎么办
在Linux中,每个CPU都对应一个软中断内核线程,名字是ksoftirqd/CPU编号,当软中断事件的频率过高时,内核线程也会因为CPU使用率过高而导致软中断处理不及时,进而引发网络收发延迟、调度缓慢等性能问题。
软中断 CPU 使⽤率过⾼也是⼀种最常⻅的性能问题。那应该怎么分析这种情况呢?
案例
准备
- 机器配置:2 CPU、8 GB 内存。
- 预先安装 docker、sysstat、sar 、hping3、tcpdump 等⼯具
- sar是一个系统活动报告工具,既可以实时查看系统的当前活动,又可以配置保存和报告历史统计数据
- hping3是一个可以构造TCP/IP协议数据包的工具,可以对系统进行安全审计、防火墙测试等
- tcpdump是一个常用的网络抓包工具,常用于分析各种网络问题
apt-get installdocker.io sysstat hping3 tcpdump。
本次案例⽤到两台虚拟机
你可以看到,其中⼀台虚拟机运⾏ Nginx ,⽤来模拟待分析的 Web 服务器;⽽另⼀台当作Web 服务器的客户端,⽤来给Nginx 增加压⼒请求。使⽤两台虚拟机的⽬的,是为了相互隔离,避免“交叉感染”。
接下来,我们打开两个终端,分别 SSH 登录到两台机器上,并安装上⾯提到的这些⼯具。
操作和分析
安装完成后,我们先在第⼀个终端,执⾏下⾯的命令运⾏案例,也就是⼀个最基本的 Nginx 应⽤:
$ docker run -itd --name=nginx -p 80:80 nginx
然后,在第⼆个终端,使⽤ curl 访问 Nginx 监听的端⼝,确认 Nginx 正常启动
$ curl http://192.168.0.30/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
.
接着,还是在第⼆个终端,我们运⾏ hping3 命令,来模拟 Nginx 的客户端请求:
$ hping3 -S -p 80 -i u100 192.168.0.30
现在我们再回到第⼀个终端,你应该发现了异常。是不是感觉系统响应明显变慢了,即便只是在终端中敲⼏个回⻋,都得很久才能得到响应?这个时候应该怎么办呢?
简单的SHELL命令都明显变慢了,那就先看看系统的整体资源使用情况,比如先执行下top看看是不是出现了CPU性能瓶颈。我们在第⼀个终端运⾏ top 命令,看⼀下系统整体的资源使⽤情况:
$ top
top - 10:50:58 up 1 days, 22:10, 1 user, load average: 0.00, 0.00, 0.00
Tasks: 122 total, 1 running, 71 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 0.0 sy, 0.0 ni, 96.7 id, 0.0 wa, 0.0 hi, 3.3 si, 0.0 st
%Cpu1 : 0.0 us, 0.0 sy, 0.0 ni, 95.6 id, 0.0 wa, 0.0 hi, 4.4 si, 0.0 st
...
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
7 root 20 0 0 0 0 S 0.3 0.0 0:01.64 ksoftirqd/0
16 root 20 0 0 0 0 S 0.3 0.0 0:01.97 ksoftirqd/1
2663 root 20 0 923480 28292 13996 S 0.3 0.3 4:58.66 docker-containe
3699 root 20 0 0 0 0 I 0.3 0.0 0:00.13 kworker/u4:0
3708 root 20 0 44572 4176 3512 R 0.3 0.1 0:00.07 top
1 root 20 0 225384 9136 6724 S 0.0 0.1 0:23.25 systemd
2 root 20 0 0 0 0 S 0.0 0.0 0:00.03 kthreadd
这里你有没有发现异常的现象?
我们从第一行开始,逐个看一下:
- 平均负载全是0,就绪队列中只有一个进程(1 running)
- 每个CPU的使⽤率都挺低,最⾼的CPU1的使⽤率也只有4.4%,并不算⾼。
- 再看进程列表,CPU使⽤率最⾼的进程也只有 0.3%,还是不⾼呀
那为什么系统的响应变慢了呢?
- 既然每个指标的数值都不大,那我们再来看看,这些指标对应的更具体的含义。毕竟,哪怕是同一个指标,用在系统的不同部位和场景上,都有可能对应着不同的性能问题。
- 仔细看 top 的输出,两个 CPU的使⽤率虽然分别只有 3.3%和4.4%,但都⽤在了软中断上;⽽从进程列表上也可以看到,CPU使⽤率最⾼的也是软中断进程 ksoftirqd。看起来,软中断有点可疑了。
既然软中断可能有问题,那么我们就先要知道,究竟是哪类软中断的问题
观察 /proc/softirqs ⽂件的内容,你就能知道各种软中断类型的次数
不过,这里的各类软中断的次数,又是什么时间段的次数呢?它是系统运行以来的累计中断次数。所以我们直接查看文件内容,得到的只是累计中断次数,对这里的问题并没有直接参考意义。因为,这些中断次数的变化速率才是我们需要关注的。
那什么工具可以观察命令的输出变化情况呢?
watch命令就可以定期运行一个命令来查看输出;如果再加上-d参数,还可以高亮变化的部分,从⾼亮部分我们就可以直观看出,哪些内容变化得更快。
在第⼀个终端,我们运⾏下⾯的命令:
$ watch -d cat /proc/softirqs
CPU0 CPU1
HI: 0 0
TIMER: 1083906 2368646
NET_TX: 53 9
NET_RX: 1550643 1916776
BLOCK: 0 0
IRQ_POLL: 0 0
TASKLET: 333637 3930
SCHED: 963675 2293171
HRTIMER: 0 0
RCU: 1542111 1590625
- 通过 /proc/softirqs ⽂件内容的变化情况,你可以发现, TIMER(定时中断)、NET_RX(⽹络接收)、SCHED(内核调度)、RCU(RCU锁)等这⼏个软中断都在不停变化
- 其中,NET_RX,也就是为了数据包接收软中断的变化速率最快。而其他几种类型的软中断,是保证Linux调度、时钟和临界区保护这些正常工具所必需的,所以他们有一定的变化时正常的。
因此,需要从网络接收的软中断着手。既然是为了接收的软中断,第一步应该就是观察系统的网络接口情况。
那么,如何观察系统的网络接口情况呢?
sar可以用来查看系统的网络收发情况,它不仅可以观察⽹络收发的吞吐量(BPS,每秒收发的字节数),还可以观察⽹络收发的 PPS,即每秒收发的⽹络帧数
$ sar -n DEV 1
15:03:46 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
15:03:47 eth0 12607.00 6304.00 664.86 358.11 0.00 0.00 0.00 0.01
15:03:47 docker0 6302.00 12604.00 270.79 664.66 0.00 0.00 0.00 0.00
15:03:47 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
15:03:47 veth9f6bbcd 6302.00 12604.00 356.95 664.66 0.00 0.00 0.00 0.05
- 第⼀列:表示报告的时间。
- 第⼆列:IFACE 表示⽹卡。
- 第三、四列:rxpck/s 和 txpck/s 分别表示每秒接收、发送的⽹络帧数,也就是 PPS。
- 第五、六列:rxkB/s 和 txkB/s 分别表示每秒接收、发送的千字节数,也就是 BPS。
- 后⾯的其他参数基本接近0,显然跟今天的问题没有直接关系,忽略掉。
我们具体来看输出的内容:
- 对⽹卡 eth0来说,每秒接收的⽹络帧数⽐较⼤,达到了 12607,⽽发送的⽹络帧数则⽐较⼩,只有 6304;每秒接收的千字节数只有 664 KB,⽽发送的千字节数更⼩,只有 358 KB。
- docker0 和 veth9f6bbcd 的数据跟 eth0 基本⼀致,只是发送和接收相反,发送的数据较⼤⽽接收的数据较⼩。这是 Linux内部⽹桥转发导致的,暂且不⽤深究,只要知道这是系统把 eth0 收到的包转发给 Nginx 服务即可
从这些数据,你有没有发现什么异常的地⽅?
既然怀疑是⽹络接收中断的问题,我们还是重点来看 eth0 :接收的 PPS ⽐较⼤,达到 12607,⽽接收的 BPS 却很⼩,只有664 KB。直观来看⽹络帧应该都是⽐较⼩的,我们稍微计算⼀下,664*1024/12607 = 54 字节,说明平均每个⽹络帧只有 54字节,这显然是很⼩的⽹络帧,也就是我们通常所说的⼩包问题。
那么,有没有办法知道这是一个什么样的网络帧,以及从哪里发过来的呢?
使⽤ tcpdump 抓取 eth0 上的包就可以了。我们事先已经知道, Nginx 监听在 80 端⼝,它所提供的 HTTP 服务是基于 TCP协议的,所以我们可以指定 TCP 协议和 80 端⼝精确抓包。
$ tcpdump -i eth0 -n tcp port 80
15:11:32.678966 IP 192.168.0.2.18238 > 192.168.0.30.80: Flags [S], seq 458303614, win 512, length 0
从 tcpdump 的输出中,你可以发现
- 192.168.0.2.18238 > 192.168.0.30.80 ,表示⽹络帧从 192.168.0.2 的 18238 端⼝发送到 192.168.0.30 的 80 端⼝,也就是从运⾏ hping3 机器的 18238 端⼝发送⽹络帧,⽬的为 Nginx 所在机器的 80 端⼝。
- Flags [S] 则表示这是⼀个 SYN 包。
再加上前⾯⽤ sar 发现的, PPS 超过 12000的现象,现在我们可以确认,这就是从 192.168.0.2 这个地址发送过来的 SYNFLOOD 攻击。
到这⾥,我们已经做了全套的性能诊断和分析。从系统的软中断使⽤率⾼这个现象出发,通过观察 /proc/softirqs ⽂件的变化情况,判断出软中断类型是⽹络接收中断;再通过 sar 和 tcpdump ,确认这是⼀个 SYN FLOOD 问题。
- 攻击者首先伪造地址对服务器发起SYN请求(我可以建立连接吗?),服务器就会回应一个ACK+SYN(可以+请确认)。而真实的IP会认为,我没有发送请求,不作回应。服务器没有收到回应,会重试3-5次并且等待一个SYN Time(一般30秒-2分钟)后,丢弃这个连接。
- 如果攻击者大量发送这种伪造源地址的SYN请求,服务器端将会消耗非常多的资源来处理这种半连接,保存遍历会消耗非常多的CPU时间和内存,何况还要不断对这个列表中的IP进行SYN+ACK的重试。TCP是可靠协议,这时就会重传报文,默认重试次数为5次,重试的间隔时间从1s开始每次都番倍,分别为1s + 2s + 4s + 8s +16s = 31s,第5次发出后还要等32s才知道第5次也超时了,所以一共是31 + 32 = 63s。
- 也就是说一共假的syn报文,会占用TCP准备队列63s之久,而半连接队列默认为1024,系统默认不同,可 cat /proc/sys/net/ipv4/tcp_max_syn_backlog c查看。也就是说在没有任何防护的情况下,每秒发送200个伪造syn包,就足够撑爆半连接队列,从而使真正的连接无法建立,无法响应正常请求。 最后的结果是服务器无暇理睬正常的连接请求—拒绝服务。
SYN FLOOD 问题最简单的解决⽅法,就是从交换机或者硬件防⽕墙中封掉来源 IP,这样 SYN FLOOD ⽹络帧就不会发送到服务器中。
案例结束后,也不要忘了收尾,记得停⽌最开始启动的 Nginx 服务以及 hping3 命令。
在第⼀个终端中,运⾏下⾯的命令就可以停⽌ Nginx 了:
$ docker rm -f nginx
然后到第⼆个终端中按下 Ctrl+C 就可以停⽌ hping3。
为什么终端会卡顿
这个是由于⽹络延迟增⼤(甚⾄是丢包)导致的。⽐如你可以再拿另外⼀台机器(也就是第三台)在 hping3 运⾏的前后 ping ⼀下案例机器,ping -c3 <ip>
hping3 运⾏前,你可能看到最⻓的也不超过 1 ms:
3 packets transmitted, 3 received, 0% packet loss, time 2028ms
rtt min/avg/max/mdev = 0.815/0.914/0.989/0.081 ms
⽽ hping3 运⾏时,不仅平均延迟增⻓到了 245 ms,⽽且还会有丢包的发⽣:
3 packets transmitted, 2 received, 33% packet loss, time 2026ms
rtt min/avg/max/mdev = 240.637/245.758/250.880/5.145 ms
软终端不⾼导致系统卡顿,我的理解是这样的,其实不是系统卡顿,⽽是由于⽤的ssh远程登录,在这期间hping3⼤量发包,导致其他⽹络连接延迟,ssh通过⽹络连接,使ssh客户端感觉卡顿现象。
总结
软中断CPU使⽤率(softirq)升⾼是⼀种很常⻅的性能问题。虽然软中断的类型很多,但实际⽣产中,我们遇到的性能瓶颈⼤多是⽹络收发类型的软中断,特别是⽹络接收的软中断。
在碰到这类问题时,你可以借⽤ sar、tcpdump 等⼯具,做进⼀步分析。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)