kworker或ksoftirqd占CPU高是内核响应压力的表象,需通过perf抓栈、/proc/softirqs、/proc/interrupts等定位真实源头,而非盲目绑核或kill。
![perf top 显示 [kworker]/[ksoftirqd] 热点时的下一步分析路径](https://img.php.cn/upload/article/001/242/473/176924286849381.jpeg)
看到 kworker 或 ksoftirqd 占 CPU 高,先别急着调优
它们不是“问题本身”,而是内核在响应某种压力的信号。比如 ksoftirqd/0 持续跑满一个核,大概率是软中断积压;kworker/1:0H 高,可能是某个设备驱动或文件系统操作在后台密集工作。直接 kill 或绑核只会掩盖根源。
- 先确认是否真异常:用
top -1看单个 CPU 核是否被长期独占(而非瞬时毛刺) - 区分类型:
ksoftirqd专处理软中断(网络收包、定时器、tasklet);kworker是通用工作队列,来源更杂(块设备、电源管理、fsnotify、驱动回调等) - 注意后缀含义:
/1:0H中的H表示 high-priority 工作队列,u8:0表示 unbound(不绑定 CPU),这类线程可能跨 NUMA 节点调度,带来额外开销
用 perf record -g 抓调用栈,定位真正触发者
perf top 只显示当前运行函数,但 ksoftirqd 的热点函数(如 __do_softirq)或 kworker 的入口(如 process_one_work)都是壳,必须看它“被谁推着跑”。
- 快速抓 10 秒:
perf record -g -e cpu-clock -- sleep 10 - 导出火焰图看源头:
perf script | ./stackcollapse-perf.pl | ./flamegraph.pl > ksoftirqd_flame.svg - 重点关注火焰图底部(leaf functions):如果是
net_rx_action→igb_poll→skb_copy_datagram_iter,就是网卡收包路径过载;如果是blk_mq_run_hw_queue→scsi_queue_rq,就要查磁盘 I/O 或存储驱动
结合系统指标交叉验证,避免误判
单靠 perf 容易断章取义。比如 kworker/u8:0 高,可能只是因为大量 inotify 事件(如 IDE 实时扫描文件),未必是性能瓶颈。
- 查软中断分布:
cat /proc/softirqs,重点看NET_RX、NET_TX、HI、TIMER列是否持续增长(对比两次采样差值) - 查硬中断来源:
cat /proc/interrupts | grep -E "(eth|enp|nvme|ioapic)",确认是否某块网卡或 NVMe 设备 IRQ 过于集中 - 查 RPS/RFS 是否启用:
cat /sys/class/net/eth0/queues/rx-0/rps_cpus,若为 0 且单核处理所有软中断,就是典型的单点瓶颈 - 查 NUMA 分布:
numastat -c ksoftirqd/0,若远端内存访问(numa_foreign)高,说明软中断处理和网卡不在同一 NUMA 节点
常见误操作与绕不开的底层约束
很多“优化”反而让问题更隐蔽。比如简单把 ksoftirqd/0 绑到 CPU 1,结果流量全涌向 CPU 1,其他核空闲——这不是均衡,是转移瓶颈。
- 不要手动
taskset绑定ksoftirqd:它的调度由内核自动完成,手动干预会破坏 softirq 负载均衡逻辑 - RPS 配置需配合网卡 RSS:仅开 RPS 不开 RSS,软中断仍全落一个 CPU;RSS 未开启硬件多队列,RPS 也无队列可分
-
kworker的高负载常来自用户态行为:比如频繁inotify_add_watch、大量sync_file_range、或容器 runtime 频繁调用 cgroup 接口,得回溯上层应用 - 某些场景无法根治:如高吞吐 DPDK 应用关闭了中断聚合,必然导致
NET_RX软中断飙升——这时应换 polling 模式,而非硬压 softirq
真正的难点从来不是怎么让 kworker 消失,而是看懂它背后那条从硬件中断、到协议栈、再到用户态应用的完整链路。漏掉任何一环,分析就只剩表象。










