深夜收到告警:某台 Nginx 网关机 CPU 使用率突破 90%,但 top 里看不到任何用户态进程占用大量 CPU。%sy 高达 60% 以上,%si 也异常突出。这不是应用代码的锅,而是 Linux 内核网络栈在向你求救。本文从一次真实排查出发,讲清软中断、ksoftirqd 与网络高负载之间的关系,并给出可落地的定位与优化手段。
一、先分清:硬中断、软中断与 ksoftirqd
网卡收到数据包时,首先触发的是硬中断。硬中断要求极快地完成,因此内核只做最紧急的事:记录中断、屏蔽网卡中断、唤醒软中断处理。真正耗时的协议栈处理(从链路层到传输层、socket 排队等)被推迟到软中断中执行。
软中断(softirq)是一组静态注册的延迟执行机制,常见类型包括:
NET_RX_SOFTIRQ:网络接收NET_TX_SOFTIRQ:网络发送TIMER_SOFTIRQ:定时器TASKLET_SOFTIRQ:taskletBLOCK_SOFTIRQ:块设备
软中断有两个执行时机:一是硬中断返回前(irq_exit)顺带执行,二是由内核线程 ksoftirqd 在后台执行。每个 CPU 都有一个 ksoftirqd 线程,命名形如 ksoftirqd/0、ksoftirqd/1。
为什么要引入 ksoftirqd?因为软中断如果一直“处理完再返回”,在高流量下会导致用户态进程长期得不到调度,甚至看门狗报 soft lockup。于是内核设定:当软中断负载过重或执行时间过长时,把剩余工作交给 ksoftirqd,让它以普通线程身份参与调度,保证公平性。
这也就解释了一个现象:网络高负载时,CPU 时间会记在 %si(软中断)和 ksoftirqd 线程上,而不是你的业务进程上。
二、现场定位:从 top 到 /proc/softirqs
1. 看整体分布
top -H
重点关注:
%si:软中断占用。持续偏高说明网络/定时器软中断压力大。%sy:内核态占用。协议栈处理、系统调用都算在这里。- ksoftirqd 线程是否出现在 CPU 占用前列。
如果 %si 高且 ksoftirqd 活跃,基本可以锁定是软中断处理不过来。
2. 看软中断类型分布
cat /proc/softirqs
这个文件按 CPU 列出各类软中断的累计次数。排查要点:
- 多次采样,观察
NET_RX的增量是否远大于其他类型。 - 对比各 CPU 的
NET_RX增量是否严重不均。
如果只有 CPU0 的 NET_RX 疯涨,说明中断亲和性配置有问题,所有网卡中断都压在了一个核上。
3. 看网卡中断分布
cat /proc/interrupts | grep -i eth
查看每个中断号在各 CPU 上的计数。若某块网卡的中断全部落在 CPU0,就是典型的单核瓶颈。
4. 看软中断的实时耗时
watch -n1 'cat /proc/softirqs'
或使用 mpstat -P ALL 1 观察每个 CPU 的 %soft 列,定位是哪颗核在扛软中断。
5. 用 perf 找到热点函数
perf top -g
在网络高负载场景下,常见热点会落在 __netif_receive_skb_core、ip_rcv、tcp_v4_rcv、net_rx_action 等函数上。这能帮你确认瓶颈确实在协议栈收包路径。
三、常见根因与对应优化
根因一:中断亲和性不合理
所有网卡中断集中在 CPU0,导致单核软中断饱和。
优化:启用 irqbalance 服务,或手动设置中断亲和性:
# 查看网卡队列对应的中断号
grep eth0 /proc/interrupts
# 将中断 128 绑定到 CPU2(掩码 0x4)
echo 4 > /proc/irq/128/smp_affinity
多队列网卡建议配合 RSS(接收端缩放),让不同队列的中断分散到不同 CPU。
根因二:单队列网卡或 RSS 未生效
如果网卡只有一个 RX 队列,无论怎么绑核都只有一个 CPU 处理收包。
优化:确认网卡是否支持多队列:
ethtool -l eth0
若支持,调整队列数:
ethtool -L eth0 combined 8
根因三:小包太多,软中断处理开销大
大量小包会让每个包都走一遍完整的软中断路径,单位字节的处理成本极高。
优化:
- 开启 GRO/GSO 合并,减少上层看到的包数量:
ethtool -K eth0 gro on gso on
- 对于 UDP 场景,考虑使用
SO_REUSEPORT让多个 socket 分担收包,或使用 XDP/AF_XDP 在更早阶段处理。 - 开启 RPS(接收包转向),把软中断处理分散到多个 CPU:
echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
RPS 适用于单队列网卡,用软件方式模拟多队列。
根因四:ksoftirqd 被饿死或调度不及时
ksoftirqd 是普通线程,如果系统里有更高优先级的实时任务长期占用 CPU,ksoftirqd 得不到调度,软中断积压。
优化:检查是否有 RT 任务霸占 CPU,必要时调整 ksoftirqd 的调度策略或优先级,但需谨慎评估。
根因五:协议栈本身成为瓶颈
在极高 PPS 场景下,即使中断分散、GRO 开启,内核协议栈仍然可能到顶。
优化方向:
- 使用
SO_REUSEPORT多进程分摊 accept 压力。 - 考虑 eBPF/XDP 在驱动层做过滤或转发,绕过部分协议栈。
- 评估 DPDK 等用户态网络方案。
四、一个可复用的排查清单
top -H确认%si高、ksoftirqd 活跃。cat /proc/softirqs确认NET_RX增量异常。cat /proc/interrupts确认中断是否集中。mpstat -P ALL 1确认是否单核%soft饱和。perf top确认热点在收包路径。- 按“中断分散 → 多队列 → GRO/RPS → 协议栈旁路”的顺序逐层优化。
五、小结
Linux 软中断与 ksoftirqd 是网络高负载下 CPU 飙升的常见“隐形推手”。它们把耗时的协议栈处理从硬中断中剥离出来,保证了系统响应性,但在流量洪峰下也会成为瓶颈。排查的核心思路是:先确认软中断类型和 CPU 分布,再定位是中断亲和性、队列数、包大小还是协议栈本身的问题,最后按层次选择绑核、多队列、GRO/RPS 或旁路方案。
理解这套机制,下次再看到 %si 飙升,你就不会只盯着业务进程发呆了。
未经允许不得转载:任鹏个人博客 » Linux 软中断与 ksoftirqd:网络高负载下的 CPU 飙升排查


朋友圈点赞图在线生成源码