软中断(softirq)是Linux内核的异步延迟处理机制,运行于内核上下文,不归属任何用户进程,因此pidstat和top无法关联到具体进程名;其统计值%si仅反映CPU在软中断处理上的时间占比,而非进程级归因。

Linux里没有“进程产生的软中断”这回事——softirq 是内核上下文,不归属任何用户进程,pidstat -u 5 或 top 里的 %si 也压根不会关联到进程名。
为什么 pidstat 和 top 都查不到进程对应的 softirq
softirq 运行在内核的 softirq context,由 ksoftirqd/CPUx 线程兜底执行,它不属于任何 task_struct;pidstat -u 统计的是进程在内核态的时间(%system),但无法区分这段时间是花在系统调用、锁竞争,还是被软中断抢占后延迟执行——它根本没采集中断向量或软中断编号。
-
top中的%si是 CPU 时间占比,只反映 softirq 占用率,不带调用栈、不带设备名、不带进程上下文 -
perf record -e irq:softirq_entry -g可以抓到软中断触发时的内核调用路径,但堆栈顶部永远是__do_softirq或net_rx_action,看不到用户进程名 - 试图用
perf record -p $(pgrep -f myapp)跟踪某进程触发的 softirq?基本无效——中断 handler 是硬件/驱动触发的,不是用户进程“调用”的
真正能间接关联进程和 softirq 的方法
虽然不能直接说“进程 A 导致了 NET_RX”,但可通过行为链反推:流量来源 → socket 绑定 → 接收队列积压 → softirq 持续调度。关键看三处:
- 查
ss -tuln或lsof -i :port,确认哪个进程监听了高流量端口;若该进程读 socket 很慢(Recv-Q持续 >0),就会导致NET_RX堆积 - 用
cat /proc/net/snmp | grep -A1 Tcp看TCPRcvQFull是否增长——说明应用没及时读,内核被迫缓存,后续全靠NET_RX消化 - 运行
watch -n 1 'cat /proc/softirqs | grep NET_RX',再同时跑mpstat -P ALL 1,如果某 CPU 的%soft高 +NET_RX增长快 + 该 CPU 上有高%system的进程,就高度可疑
别碰的误区:perf + filter 以为能锁定进程
有人试过:perf record -e irq:softirq_entry --filter "vec == 3" -C 0(vec == 3 是 NET_RX 编号),再 perf report -g 看调用栈。结果堆栈里确实能看到 tcp_v4_do_rcv → sk_receive_skb → sock_queue_rcv_skb,但往上再也找不到用户进程符号——因为数据包从网卡进来那一刻,就脱离了用户态控制流。
- 软中断不是“被进程触发”,而是“为进程服务”;它响应的是硬件事件(包到达),不是 syscall
- 想让某个进程少刷
NET_RX?得让它更快读 socket,或者改用AF_XDP/io_uring绕过内核协议栈,而不是去“查谁在打中断” - 真要归因,必须从流量源头开始:
tc -s class show dev eth0看队列堆积点,ethtool -S eth0 | grep rx_看丢包/重试,再结合应用层 socket 状态
最常被忽略的一点:当 NET_RX 单核飙高时,问题往往不在中断本身,而在那个没及时调用 recv() 的进程——它不显式出现在中断统计里,却实实在在把软中断变成了瓶颈。盯住 /proc/net/snmp 里的 TCPRcvQFull 和 ss -i 的 rcv_space,比翻 /proc/softirqs 更早发现问题根源。


















