凌晨2点17分,手机报警把我从梦里拽出来。客户反馈线上业务全部卡死,登录服务器一看,load average 直接飙到 80 多,top 里 CPU 的 si(软中断)占用超过 90%,整个系统像被人按住了脖子。我第一反应就是:这他妈是中断风暴。
干运维和内核排查这么多年,中断风暴算是“看起来吓人、其实有套路”的典型故障。只要你能冷静下来,按顺序把 /proc/interrupts、/proc/softirqs、mpstat 这几个工具过一遍,大多能在十分钟内定位到真凶。这篇文章我把我实战中遇到过的中断风暴场景、排查思路、止血手段、长期治理方案全部整理出来,希望能帮你少走点弯路。
1. 先搞清楚什么是中断风暴
1.1 中断机制的基本原理
要理解中断风暴,得先回到中断本身。CPU 和硬件设备之间通信,总不能靠 CPU 不停轮询设备“你有没有事”,那样太浪费资源了。所以硬件设备有了事件(网卡收到数据包、磁盘完成 IO、定时器到期),会主动拉高一条中断线,通知 CPU“过来处理我”。CPU 收到这个信号,会暂停当前正在执行的任务,跳转到对应的中断处理程序,处理完再回来继续干活。
这个机制本身没问题,问题出在“频率”上。正常情况下一块千兆网卡每秒产生几千到几万个中断是很正常的,CPU 完全扛得住。但如果因为驱动 bug、硬件故障、配置错误,导致中断以每秒几十万甚至上百万次的频率疯狂触发,CPU 就会把绝大部分时间都花在处理中断上,真正该干活的进程反而抢不到 CPU 时间片。这就是中断风暴,本质上是“中断处理”本身成了系统最大的负担。
我习惯用一个生活化的比喻来解释:想象你正在专心写代码,每过几秒就有一个同事过来问你一个非常简单的问题。偶尔问一次没问题,但如果突然有几百个同事排队来问你“这个按钮放哪”“那个字体用几号”,你这一天啥也干不了,净回答问题了。中断风暴就是这个场景。
1.2 中断风暴的典型表现和危害
中断风暴最典型的表现就是一个字:卡。具体症状通常是这几个:
系统 load average 飙升,经常超过 CPU 核数的好几倍;top 里能看到 CPU 的 si 和 hi 占用极高,si 是软中断,hi 是硬中断;业务响应超时,Ping 能通但 SSH 敲命令明显延迟;/proc/interrupts 里某个中断号的计数增长极其离谱,几秒钟就增加几百万。
危害不只是“卡一下”。极端情况下,中断风暴会导致 CPU 长时间停留在中断上下文里,watchdog 机制会认为系统已经 hang 住,直接触发内核 panic 或者重启。如果集群里有其他节点分担压力还好,如果是单点,那就是妥妥的生产事故。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 排查中断风暴的完整工具箱
2.1 第一现场:/proc/interrupts 和 mpstat 配合使用
排查中断风暴的第一步,是先确认“风暴是不是真的存在”,然后找到是哪个中断号、哪个 CPU 在被轰炸。
最直接的入口就是 /proc/interrupts。这个虚拟文件会列出系统中每个中断号、每个 CPU 上的触发次数、中断名称以及对应的设备驱动。我通常先执行两次 cat,中间间隔 2 秒,对比中断计数的增长量。
bash复制cat /proc/interrupts
sleep 2
cat /proc/interrupts
比如某次我在现场看到的结果大致是这种形态:
text复制 CPU0 CPU1 CPU2 CPU3
24: 980245213 984125632 976845231 982154782 PCI-MSI 458752-edge enp3s0-rx-0
25: 124 133 121 145 PCI-MSI 458753-edge enp3s0-rx-1
26: 45 52 48 55 PCI-MSI 458754-edge enp3s0-rx-2
注意看,所有 CPU 上的中断计数都在飞速上涨,而且集中在 enp3s0-rx-0 这个网卡接收队列上,大概率就是网卡收包路径出问题了。为了确认这个判断,我再用 mpstat 看每颗 CPU 的中断分布:
bash复制mpstat -P ALL 2
输出里如果看到某颗 CPU 的 %soft 达到 80% 以上,同时 /proc/interrupts 里对应的 CPU 列计数飙升,那就锁定了“被轰炸的 CPU 和中断来源”。这个组合拳基本能快速定位到“中断风暴发生在哪个设备上”。
2.2 进阶定位:/proc/softirqs、top 和 perf
硬中断定位到了,还得看软中断。因为现代驱动普遍采用 NAPI 机制,网卡硬中断触发后,真正干活的反而是软中断(NET_RX_SOFTIRQ)。所以 /proc/softirqs 也要重点盯住。
bash复制cat /proc/softirqs
sleep 2
cat /proc/softirqs
对比两次输出,重点看 NET_RX、NET_TX、TIMER 这几个字段的增长速度。如果 NET_RX 每秒增长百万级别,基本就是网络收包路径被什么东西打爆了。如果是 TIMER 字段飙升,那要怀疑是不是有高频定时器在搞鬼。
top 命令也不能只看 load。按 1 切换到每颗 CPU 的视图,会看到类似这样的字段:us(用户态)、sy(内核态)、si(软中断)、hi(硬中断)。当 si 超过 50%,基本可以断定软中断已经吃掉了大部分 CPU 资源。
更进一步的定位,用 perf 抓一下中断上下文里的热点函数:
bash复制perf top -G
perf 可以告诉我们 CPU 花在哪个函数上的时间最多。我之前遇到过一次网络中断风暴,perf top 直接指向了驱动的某个收包函数,顺着这个线索去查驱动版本和已知 bug,很快就找到了根因。如果 perf 不太熟,退一步用 cat /proc/interrupts + 查看 dmesg 里有没有硬件报错也够用。
3. 核心场景拆解:我踩过的三种中断风暴
3.1 网卡收包风暴:最常见也最头疼
网卡收包导致的中断风暴,我遇到的最多,而且原因五花八门。最常见的是网卡多队列配置不当,导致所有流量都挤到同一个队列上。比如网卡有 8 个队列,但中断都绑到了 CPU0,大量数据包进来后 CPU0 直接被击穿,其他 CPU 在旁边看戏。
另一种情况是驱动 bug 导致收包中断无法正确合并(coalesce),每收到一个小包就触发一次中断,小包场景下(比如大量 TCP ACK、DNS 查询),中断频率直接爆炸。
还有一种是网卡硬件故障或者链路异常,导致网卡不断报错、不断触发中断。我在 /proc/interrupts 里见过某块网卡的错误中断计数几分钟涨几百万的情况。
3.2 硬件故障导致中断反复触发
硬件故障引发的中断风暴,往往更隐蔽。最典型的是磁盘和 PCIe 设备。
某次线上存储节点出问题,现象是系统响应极慢,top 里 sys 很高。排查 /proc/interrupts 后发现,一个来自磁盘控制器(比如 megaraid_sas 或者 nvme)的中断计数飙升。进一步看 dmesg,发现大量的 IO 错误、设备重置日志。这个其实是磁盘或者阵列卡在反复报错,每次报错触发一次中断,堆积起来就成了风暴。
更麻烦的是 PCIe AER(Advanced Error Reporting)中断风暴。当某个 PCIe 设备出现链路错误,系统会不断收到 AER 中断,触发几千上万条错误日志,这个场景在硬件老化或者板卡接触不良时特别容易出现。某次我看到 dmesg 里刷屏的 AER 错误,顺着查到是一块 GPU 计算卡的 PCIe 链路不稳定,重新插拔之后才恢复。
3.3 高频定时器和内核 bug 导致的软中断风暴
软中断风暴不一定来自网络。有一次内部测试环境的 CPU 莫名其妙飙高,si 占满所有核心。我查 /proc/interrupts 发现硬中断计数很平稳,但 /proc/softirqs 里的 TIMER 字段增长惊人,每秒几百万次。
这个其实就是典型的高频定时器风暴。某些内核版本或者驱动在特定场景下会疯狂注册高精度定时器,导致 CPU 在定时器软中断里反复醒来。那次最后定位到是某个内核版本和特定 CPU 型号的兼容问题,升级内核之后就好了。
内核 bug 引发的中断风暴还有一个经典场景:网卡驱动在收到特定类型报文时进入死循环,不断提交新的收包缓冲处理,触发持续不断的软中断。这种问题最麻烦,因为表面上看流量不大,但 CPU 的 si 已经打满了。
4. 解决与预防:从救火到建设
4.1 快速止血的手段
遇到中断风暴,第一目标不是找根因,而是先把业务救回来。止血手段按优先级排列大概是这样的:
把被中断轰炸的 CPU 上的业务进程迁移走,或者直接隔离出问题 CPU。用 taskset 可以把关键进程绑到没被影响的 CPU 上。
临时关掉触发风暴的设备中断,比如:
bash复制echo 0 > /sys/class/net/enp3s0/device/irq
这个操作是直接把网卡中断关了,但不太建议在生产环境乱试,因为关掉中断之后收包会完全依赖轮询,反而可能更糟。我更推荐的方法是直接 down 掉业务入口,比如临时摘掉负载均衡里的节点,哪怕短暂中断,也比重启整个服务器强。
对于网络软中断风暴,可以通过调整内核参数暂缓一下:
bash复制sysctl -w net.core.netdev_budget=600
sysctl -w net.core.netdev_budget_usecs=8000
netdev_budget 是软中断一次最多处理的包数量,默认是 300;netdev_budget_usecs 是最多处理时间,默认 8000 微秒。适当调大这两个参数,可以让 CPU 每次进入软中断后多处理一会,减少反复触发软中断的频率。但这只是延缓,不能根治。
4.2 中长期治理:irqbalance、RPS/RFS 和中断合并
救火之后就得认真治理了。长期方案里,第一件事是确保 irqbalance 服务在运行。irqbalance 会动态把中断分配到各个 CPU 上,避免单核打满。如果机器上关了 irqbalance,请务必确认你清楚自己在干什么。
bash复制systemctl enable --now irqbalance
对于多队列网卡,最理想的方案是用 RPS(Receive Packet Steering)和 RFS(Receive Flow Steering)把软中断处理分散到多个 CPU。RPS 在软件层模拟多队列的效果,RFS 进一步保证同一个流的包都在同一个 CPU 上处理,兼顾并发和缓存命中率。
bash复制echo "ffffffff" > /sys/class/net/enp3s0/queues/rx-0/rps_cpus
这个 ffffffff 表示把处理能力扩展到前 32 个 CPU(按位图对应)。配置 RPS 之前,先确认网卡本身是不是已经有多队列了,如果硬件队列本来就够,RPS 可能反而引入额外的开销。
中断合并(coalesce)也很重要。现代网卡基本都支持把多个小包攒成一次中断,用 ethtool 调节:
bash复制ethtool -C enp3s0 rx-usecs 100 rx-frames 32
rx-usecs 是延迟多少微秒再产生中断,rx-frames 是攒够多少个包再中断。这个参数要小心调整,调太大会增加延迟,调太小又起不到合并效果。对于延迟敏感的业务(比如 Redis、数据库),我一般把 rx-usecs 控制在 50 以内;对于高吞吐、对延迟不敏感的业务,可以放到 100 甚至更高。
4.3 驱动、固件和系统版本:容易被忽略的根因
排查中断风暴时,很多人会被内核参数和各种调优吸引走注意力,反而忽略了最基础的驱动和固件版本。
我在实际工作中遇到过两个典型案例。第一个是某型号万兆网卡在特定驱动版本下,处理 64 字节小包时会产生极高的中断频率,升级驱动后问题直接消失;第二个是 NVMe 固态硬盘的固件有 bug,在特定 IO 模式下反复触发控制器中断,刷新固件后恢复正常。
所以一旦定位到某个设备的中断异常,立刻做三件事:查驱动版本、对比厂商 Release Notes、看是否有已知问题修复。这在 Red Hat 和 Ubuntu 的官方知识库、厂商社区里都有大量文档可以参考。不要一上来就调内核参数,调了半天可能只是掩盖了驱动 bug。
5. 常见问题与排查技巧实录
5.1 典型案例速查表
我把这些年遇到过的中断风暴案例整理成一个速查表,希望能帮你快速对号入座。
| 典型症状 | 可能原因 | 核心排查命令 | 解决方向 |
|---|---|---|---|
| 网卡某个 rx 队列中断计数暴涨,si 占满单核 | 多队列绑定不均 / 驱动收包 bug / 小包攻击 | cat /proc/interrupts,mpstat -P ALL | 调整 irqbalance,RPS 分散,升级驱动 |
| 磁盘控制器中断飙升,伴随 IO 报错 | 磁盘故障 / 阵列卡重置 | dmesg 看 IO 错误,smartctl 查磁盘健康 | 更换硬件,检查背板和线缆 |
| TIMER 软中断暴涨,硬中断正常 | CPU 高频定时器 / 内核兼容问题 | cat /proc/softirqs 对比 TIMER 字段 | 升级内核或驱动,排查高精度定时器相关配置 |
| PCIe AER 错误刷屏,伴随中断飙升 | PCIe 链路不稳定 / 板卡松动 | dmesg grep AER,lspci -vvv | 重新插拔硬件,升级固件,更换插槽 |
| 网卡中断计数正常,但 NET_RX 软中断暴涨 | 驱动关闭了 NAPI 合并 / 流量突增 | cat /proc/softirqs,ethtool -S | 调整中断合并参数,扩容网卡队列 |
5.2 踩过的坑和实用的排查技巧
第一,别盲目关 irqbalance。有些资料说 irqbalance 会把中断搬来搬去导致性能抖动,然后建议关掉手动绑核。但如果你不知道该绑哪个核、绑错了反而更糟。自动调度的代价远小于人工拍脑袋。真遇到性能要求极高的场景,也应该先压测对比再决定。
第二,cat /proc/interrupts 的时候一定要学会看懂“中断号在迁移”的表现。irqbalance 在后台运作时,中断计数在不同 CPU 间漂移是正常的。如果你看到某个中断在多个 CPU 间反复横跳,先别急着骂系统,这恰恰说明 irqbalance 正在工作,是健康表现。
第三,kworker 进程占用高和中断风暴经常同时出现,但别搞混因果关系。很多时候是驱动把大量工作量扔给了 kworker,表现为 kworker 的 CPU 占用很高。这时候要顺着 /proc/interrupts 找到关联设备和驱动,而不是跟 kworker 死磕。
第四,一个小技巧:排查时优先看 dmesg,因为很多导致中断风暴的原因(设备报错、驱动 panic、PCIe AER)都会在这里留下痕迹。我习惯在确认风暴之后立刻执行 dmesg -T | tail -200 看最近的内核日志,几十秒就能确定是不是硬件报错导致的,节省大量时间。
第五,不要忽略 irq 亲和性配置的持久化。很多人在系统运行时手动设置 /proc/irq/xxx/smp_affinity,重启后就失效了。如果确定要手动绑核,必须在网络管理工具或者 udev 规则里固化配置,否则下次重启中断分配又乱了,你会误以为中断风暴又复发了。
5.3 一套可以直接抄的排查脚本思路
最后分享一个我个人的排查脚本思路,核心是“先取证、再分析”。风暴发生的时候,现场数据比什么都重要,一旦系统重启,很多线索就没了。脚本逻辑大概是:
bash复制echo "===== date =====" ; date
echo "===== load =====" ; uptime
echo "===== mpstat =====" ; mpstat -P ALL 1 5
echo "===== interrupts =====" ; cat /proc/interrupts
echo "===== softirqs =====" ; cat /proc/softirqs
echo "===== dmesg =====" ; dmesg -T | tail -200
echo "===== top =====" ; top -b -n 1 | head -30
这套东西放在一个脚本里,出现问题直接执行,把输出保存下来。数据样本越完整,事后分析越轻松。我甚至建议把这些输出自动同步到日志平台,因为中断风暴往往不是靠现场盯出来的,而是靠事后还原出来的。
6. 从一次真实故障讲讲完整复盘
为了把前面说的都串起来,我拿一次真实故障做完整复盘。
那是某客户的一套业务集群,某天下午突然出现 CPU 软中断飙升,业务接口 P99 延迟从 50ms 涨到 3 秒。我先在出问题的机器上执行了上面的排查脚本。mpstat 显示 CPU2 的 %soft 高达 90%,/proc/interrupts 显示 enp3s0-rx-2 这个网卡队列的中断计数在两秒内从 8 亿涨到 10 亿,增长幅度异常。
紧接着我看了 dmesg,没发现硬件报错,排除了设备故障。ethool -S 查看网卡统计,发现 rx_missed 和 rx_dropped 快速增加,说明网卡已经收不过来了。这时候我判断是网卡单队列被打爆,导致中断风暴。
随即执行的止血动作是调整 netdev_budget 从 300 到 600,同时把 irqbalance 启动并确认它把中断分散到了多个 CPU。几分钟后,CPU2 的 si 从 90% 降到 40% 左右,业务延迟回到 100ms 以内。虽然还没到最优状态,但至少业务恢复了。
后面深入排查才找到真正的流量来源:一台业务机器上的日志采集 agent 异常,疯狂往日志服务器发送小包,占满了网卡队列。把 agent 重启后,中断恢复正常。这次事故的根因其实不是系统配置,而是业务侧的小包洪峰,但能快速止血靠的是对中断风暴排查的熟练程度。
这个案例里有个细节值得多说一句:dmesg 没报错不代表硬件没问题。有些网卡在达到处理上限时并不会主动报错,而是反复尝试、反复触发中断,表现出来就是纯粹的中断风暴。所以即使 dmesg 干净,也不能轻易排除网卡性能瓶颈。
根据我个人的经验,中断风暴并不神秘,它的排查路径非常清晰:看 /proc/interrupts 找中断源,看 mpstat 找受害 CPU,看 /proc/softirqs 确认软中断类型,看 dmesg 排除硬件故障。只要按这个顺序走,大部分情况都能在几分钟内定位。真正难的不是排查,而是在紧急时刻保持冷静,别一上来就重启服务器把现场证据全毁了。记住一个原则:先取证,再动手。把最重要的 /proc/interrupts 和 dmesg 输出保留下来,你就已经赢了一半。
