1. 概述
中断是一种异步的事件处理机制,用来提高系统的并发处理能力。中断事件发生,会触发执行中断处理程序,而中断处理程序被分为上半部和下半部这两个部分。
- 上半部对应硬中断,用来快速处理中断;
- 下半部对应软中断,用来异步处理上半部未完成的工作。
Linux 中的软中断包括网络收发、定时、调度、RCU 锁等各种类型,我们可以查看 proc 文件系统中的 /proc/softirqs ,观察软中断的运行情况。
当软中断事件的频率过高时,内核线程也会因为 CPU 使用率过高而导致软中断处理不及时,进而引发网络收发延迟、调度缓慢等性能问题。
2. 案例
环境准备:
- Ubuntu 18.04
- 机器配置:2 CPU、8 GB 内存。
- 预先安装 docker、sysstat、sar 、hping3、tcpdump 等工具,比如 apt-get install docker.io sysstat hping3 tcpdump。
三个新工具,sar、 hping3 和 tcpdump,先简单介绍一下:
- sar 是一个系统活动报告工具,既可以实时查看系统的当前活动,又可以配置保存和报告历史统计数据。
- hping3 是一个可以构造 TCP/IP 协议数据包的工具,可以对系统进行安全审计、防火墙测试等。
- tcpdump 是一个常用的网络抓包工具,常用来分析各种网络问题。
本次案例用到两台虚拟机(IP仅供参考)
VM 1执行
# 运行Nginx服务并对外开放80端口
$ docker run -itd --name=nginx -p 80:80 nginx
VM 2执行,IP填写VM 1
$ curl http://172.30.104.143/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
继续VM 2执行
# -S参数表示设置TCP协议的SYN(同步序列号),-p表示目的端口为80
# -i u100表示每隔100微秒发送一个网络帧
# 注:如果你在实践过程中现象不明显,可以尝试把100调小,比如调成10甚至1
$ hping3 -S -p 80 -i u100 172.30.104.143
此时会发现VM 1会有点卡(我的机器没有出现,因为我是在我的物理机上部署了两个VM,并且本地SSH连过去,基本上没有网络瓶颈,如果想复现VM 1卡顿现象,可以在远端Server部署两台虚拟机,通过VPN或者其他网络不是很好的方式通过SSH连接,会复现。),不影响分析案例
先用top查看资源整体情况
# top运行后按数字1切换到显示所有CPU
$ 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%,使用率不高。
唯独ksoftirqd比较可疑。既然是软中断,那就查看/proc/softirqs
$ watch -d cat /proc/softirqs
-d表示高亮变化的部分。 TIMER(定时中断)、NET_RX(网络接收)、SCHED(内核调度)、RCU(RCU 锁)等这几个软中断都在不停变化。其中,NET_RX,也就是网络数据包接收软中断的变化速率最快。
既然与网络相关,那么可以使用今天的新工具 sar
# -n DEV 表示显示网络收发的报告,间隔1秒输出一组数据
$ 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的接收PPS达到了13264、但是包大小只有60KB。典型的小包传送。此时需要进一步知道是什么样的包,传了什么,就需要用到tcpdump抓包。
# -i eth0 只抓取eth0网卡,-n不解析协议名和主机名
# tcp port 80表示只抓取tcp协议并且端口号为80的网络帧
$ tcpdump -i eth0 -n tcp port 80
对于网络不是很熟悉,借助一下AI
Flags [S] 则表示这是一个 SYN 包。
继续询问
好吧,后面大家可以执行尝试。对于我这种网络小白,有了GPT之后友好都了。。。那如何解决呢?
SYN FLOOD 问题最简单的解决方法,就是从交换机或者硬件防火墙中封掉来源 IP,这样 SYN FLOOD 网络帧就不会发送到服务器中。
回收实验环境
# 停止 Nginx 服务
$ docker rm -f nginx
VM 2上的hping3也要停掉。
附录:
在查看软中断中,每个核是按列来显示的,如果存在及时个核心的场景看起来会比较困难,此时可以借助下面的命令
watch -d "/bin/cat /proc/softirqs | /usr/bin/awk 'NR == 1{printf "%-15s %-15s %-15s %-15s %-15s\n"," ",$1,$2,$3,$4}; NR > 1{printf "%-15s %-15s %-15s %-15s %-15s\n",$1,$2,$3,$4,$5}'"
还有一个需要关注,top命令中,我们可以看到 si 也就是软中断全部发生在CPU 1上面,此时网络转发就会成为瓶颈,在多核处理器上可以通过 配置 smp_affinity 或者开启 irqbalance 服务 将网络中断配置在多个核中。
参考命令如下:
echo "3" > /sys/class/net/eth0/queues/rx-0/rps_cpus
- 单核
1表示 CPU 0。 - 双核
3表示 CPU 0 和 1。 - 四核
f表示 CPU 0、1、2、3。