系统的软中断CPU使用率升高,我该怎么办?
- 前言
- 案例
- 操作和分析
- 总结
前言
中断是一种异步的事件处理机制,用来提高系统的并发处理能力。中断事件发生,会触发执行中断处理程序,而中断处理程序被分为上半部和下半部这两个部分。
- 上半部对应硬中断,用来快速处理中断
- 下半部对应软中断,用来异步处理上半部未完成的工作
Linux 中的软中断包括网络收发、定时、调度、RCU 锁等各种类型,我们可以查看 proc 文件系统中的 /proc/softirqs ,观察软中断的运行情况。
在 Linux 中,每个 CPU 都对应一个软中断内核线程,名字是 ksoftirqd/CPU 编号。当软 中断事件的频率过高时,内核线程也会因为 CPU 使用率过高而导致软中断处理不及时,进 而引发网络收发延迟、调度缓慢等性能问题。
软中断 CPU 使用率过高也是一种最常见的性能问题。下面我们就用最常见的反向代理服务器 Nginx 的案例,来学会分析这种情况。
案例
预先安装 docker、sysstat、sar 、hping3、tcpdump 等工具。
- sar 是一个系统活动报告工具,既可以实时查看系统的当前活动,又可以配置保存和报 告历史统计数据。
- hping3 是一个可以构造 TCP/IP 协议数据包的工具,可以对系统进行安全审计、防火墙 测试等。
- tcpdump 是一个常用的网络抓包工具,常用来分析各种网络问题。
本次案例用到两台机器,用一张图来表示它们的关系。
可以看到,其中一台虚拟机运行 Nginx ,用来模拟待分析的 Web 服务器;而另一台当 作 Web 服务器的客户端,用来给 Nginx 增加压力请求。使用两台虚拟机的目的,是为了 相互隔离,避免“交叉感染”。
接下来,我们打开两个终端,分别 SSH 登录到两台机器上,并安装上面提到的这些工具。
操作和分析
在客户端机,使用 curl 访问 Nginx 监听的端口,确认 Nginx 正常启动。
# curl http://192.91.16.101
<!DOCTYPE html>
<html>
<head>
<title>Nginx</title>
<meta charset="utf-8">
<meta name="author" content="Licess">
</head>
<body>
<div id="main">
<div align="center"><b欢迎访问nginx</b></div>
</div>
</body>
</html>
接着,还是在第客户端机,我们运行 hping3 命令,来模拟 Nginx 的客户端请求:
# -S 参数表示设置 TCP 协议的 SYN(同步序列号),-p 表示目的端口为 80
# -i u100 表示每隔 100 微秒发送一个网络帧
# 注:如果你在实践过程中现象不明显,可以尝试把 100 调小,比如调成 10 甚至 1
hping3 -S -p 80 -i u100 192.168.0.30
再回到第一个终端,你应该发现了异常。是不是感觉系统响应明显变慢了,即便只是在终端中敲几个回车,都得很久才能得到响应?这个时候应该怎么办呢?
那么,该从什么地方入手呢?刚才我们发现,简单的 SHELL 命令都明显变慢了,先看看系 统的整体资源使用情况应该是个不错的注意,比如执行下 top 看看是不是出现了 CPU 的 瓶颈。我们在第一个终端运行 top 命令,看一下系统整体的资源使用情况。
top
Tasks: 146 total, 2 running, 144 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.0 us, 0.3 sy, 0.0 ni, 50.0 id, 0.0 wa, 3.7 hi, 46.0 si, 0.0 st
%Cpu1 : 0.0 us, 0.0 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.3 hi, 0.0 si, 0.0 st
MiB Mem : 3637.6 total, 639.2 free, 811.5 used, 2187.0 buff/cache
MiB Swap: 4096.0 total, 4091.2 free, 4.8 used. 2554.3 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
9 root 20 0 0 0 0 R 0.3 0.0 0:02.58 ksoftirqd/0
10 root 20 0 0 0 0 I 0.3 0.0 4:18.81 rcu_sched
805 root 20 0 233936 5936 5108 S 0.3 0.2 2:29.64 rngd
32087 root 10 -10 178848 33376 15456 S 0.3 0.9 109:22.08 AliYunDun
1 root 20 0 176960 10692 8040 S 0.0 0.3 1:17.62 systemd
....
这里我们有没有发现异常的现象?我们从第一行开始,逐个看一下:
平均负载全是 0,就绪队列里面只有一个进程(1 running)。
每个 CPU 的使用率都挺低,最高的 CPU1 的使用率也只有 4.4%,并不算高。 再看进程列表,CPU 使用率最高的进程也只有 0.3%,还是不高呀。
那么我们使用sar 命令来看一下网络收发的报告:
# -n DEV 表示显示网络收发的报告,间隔 1 秒输出一组数据
sar-n DEV 1
Linux 4.18.0-147.5.1.el8_1.x86_64 (service) 2021年07月02日 _x86_64_ (2 CPU)
15时09分17秒 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
15时09分18秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
15时09分18秒 eth0 15542.00 7773.00 910.68 444.02 0.00 0.00 0.00 0.00
15时09分18秒 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
15时09分19秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
15时09分19秒 eth0 15541.00 7771.00 910.61 440.28 0.00 0.00 0.00 0.00
15时09分19秒 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
15时09分20秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
15时09分20秒 eth0 15202.00 7602.00 890.75 431.15 0.00 0.00 0.00 0.00
15时09分20秒 IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s %ifutil
15时09分21秒 lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
15时09分21秒 eth0 15565.00 7784.00 912.00 441.44 0.00 0.00 0.00 0.00
对于 sar 的输出界面,我先来简单介绍一下,从左往右依次是:
- 第一列:表示报告的时间。
- 第二列:IFACE 表示网卡。
- 第三、四列:rxpck/s 和 txpck/s 分别表示每秒接收、发送的网络帧数,也就是 PPS。
- 第五、六列:rxkB/s 和 txkB/s 分别表示每秒接收、发送的千字节数,也就是 BPS。
我们具体来看输出的内容,可以发现:
对网卡 eth0 来说,每秒接收的网络帧数比较大,达到了 15565.00,而发送的网络帧数则 比较小,只有 7784.00;每秒接收的千字节数只有 912 KB,而发送的千字节数更小,只有 441.44 KB。
既然怀疑是网络接收中断的问题,我们还是重点来看 eth0 :接收的 PPS 比较大,达到 15565.00,而接收的 BPS 却很小,只有 912 KB。直观来看网络帧应该都是比较小的,我们 稍微计算一下,912*1024/15565 = 59 字节,说明平均每个网络帧只有 59 字节,这显然是很小的网络帧,也就是我们通常所说的小包问题。
那么,有没有办法知道这是一个什么样的网络帧,以及从哪里发过来的呢?
使用 tcpdump 抓取 eth0 上的包就可以了。我们事先已经知道, Nginx 监听在 80 端口,它所提供的HTTP 服务是基于 TCP 协议的,所以我们可以指定 TCP 协议和 80 端口 精确抓包。
接下来,我们在第一个终端中运行 tcpdump 命令,通过 -i eth0 选项指定网卡 eth0,并 通过 tcp port 80 选项指定 TCP 协议的 80 端口:
[root@ default]# tcpdump -i eth0 -n tcp port 80
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
15:38:44.003066 IP 47.243.28.161.24559 > 172.31.36.79.http: Flags [R], seq 1163136593, win 0, length 0
15:38:44.003181 IP 47.243.28.161.24569 > 172.31.36.79.http: Flags [S], seq 621341255, win 512, length 0
15:38:44.003187 IP 172.31.36.79.http > 47.243.28.161.24569: Flags [S.], seq 3012841219, ack 621341256, win 29200, options [mss 1460], length 0
15:38:44.003192 IP 47.243.28.161.24560 > 172.31.36.79.http: Flags [R], seq 406564800, win 0, length 0
....
从 tcpdump 的输出中,可以发现:
47.243.28.161.24559 > 172.31.36.79,表示网络帧从 47.243.28.161 的 24559 端口发送到 172.31.36.79 的 80 端口,也就是从运行 hping3 机器的 24559 端口发送网络 帧,目的为 Nginx 所在机器的 80 端口。
再加上前面用 sar 发现的, PPS 超过 15565 的现象,现在我们可以确认,这就是从 47.243.28.161 这个地址发送过来的 SYN FLOOD 攻击。
总结
软中断 CPU 使用率(softirq)升高是一种很常见的性能问题。虽然软中断的类型很多, 但实际生产中,我们遇到的性能瓶颈大多是网络收发类型的软中断,特别是网络接收的软中断。在碰到这类问题时,你可以借用 sar、tcpdump 等工具,做进一步分析。