hardirq 要快——关中断时间越短越好。延迟工作交给下半部(bottom half)。Linux 有三条路。
一、先看图
flowchart TD
HARDIRQ[硬中断 handler<br/>最小化工作] --> RAISE[raise_softirq<br/>或 schedule_work]
RAISE --> SOFTIRQ[softirq<br/>静态 10 个<br/>原子上下文]
RAISE --> TASKLET[tasklet<br/>基于 softirq<br/>退役中]
RAISE --> WQ[workqueue<br/>内核线程<br/>可睡眠]
SOFTIRQ --> KSD[ksoftirqd<br/>长尾时接管]
classDef hard fill:#f85149,stroke:#f85149,color:#adbac7;
classDef soft fill:#388bfd22,stroke:#388bfd,color:#adbac7;
classDef wq fill:#3fb95022,stroke:#3fb950,color:#adbac7;
class HARDIRQ hard
class RAISE,SOFTIRQ,TASKLET,KSD soft
class WQ wq
二、softirq
2.1 静态编号
enum {
HI_SOFTIRQ,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
IRQ_POLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ,
};只有 10 个固定编号——不能动态添加。
2.2 执行
hardirq 返回时检查 pending softirq →
__do_softirq() 处理。
特点:
- 原子上下文(不能睡眠)
- 同一 softirq 可以在多个 CPU 并行
- 高优先级
2.3 ksoftirqd
softirq 处理时间过长 → 避免饿死用户进程 → 切换到
ksoftirqd 内核线程(以 nice 19 运行)。
三、tasklet
DECLARE_TASKLET(my_tasklet, my_func);
tasklet_schedule(&my_tasklet);基于 TASKLET_SOFTIRQ,动态创建。
特点:
- 同一 tasklet 不会在多 CPU 并行(串行化)
- 原子上下文
退役趋势:内核社区正在移除 tasklet,替换为 workqueue 或 threaded IRQ。
四、workqueue
DECLARE_WORK(my_work, my_func);
schedule_work(&my_work); // 系统默认 workqueue
queue_work(my_wq, &my_work); // 自定义 workqueue4.1 类型
| 类型 | 特点 |
|---|---|
| bound | 绑定到提交 CPU |
| unbound | 可在任意 CPU 运行 |
| ordered | 串行执行 |
| WQ_POWER_EFFICIENT | 低功耗(可能迁移) |
4.2 线程池
workqueue 使用 kworker 线程池——不是每
workqueue 一个线程。
ps aux | grep kworker4.3 可睡眠
workqueue handler 可以睡眠 → 可以分配内存、做 I/O、获取 mutex。
五、threaded NAPI
网络收包高频 → softirq 长尾 → ksoftirqd 延迟高。
Threaded NAPI(5.x+):每 NAPI 实例一个内核线程 → 可调度、可设优先级。
echo 1 > /sys/class/net/eth0/threaded六、选择指南
| 场景 | 推荐 |
|---|---|
| 网络收发 | softirq(NET_RX/TX) |
| 块 I/O 完成 | softirq(BLOCK) |
| 设备驱动通用 | workqueue 或 threaded IRQ |
| 需要睡眠 | workqueue |
| 旧代码 tasklet | 迁移到 workqueue |
七、softirq 长尾问题
# 观察 softirq CPU 占用
cat /proc/softirqs
mpstat -P ALL 1 | grep -i softNET_RX 占用过高 → 考虑:
- 增加 NIC 队列数
- 开启 busy polling
- 使用 threaded NAPI
八、delayed_work
DECLARE_DELAYED_WORK(my_dwork, my_func);
schedule_delayed_work(&my_dwork, msecs_to_jiffies(100));延迟执行 + workqueue 能力。
九、观察
cat /proc/softirqs # per-CPU softirq 计数
cat /proc/interrupts # hardirq 计数
ps -eo pid,comm | grep kworker # workqueue 线程
perf top -e irq:softirq_entry # softirq 热点十、小结
- softirq:静态 10 个、高性能、原子上下文
- tasklet:退役中,迁移到 workqueue
- workqueue:灵活、可睡眠、线程池
- ksoftirqd 处理 softirq 长尾
- threaded NAPI 是网络场景的新方向
参考文献
kernel/softirq.ckernel/workqueue.cDocumentation/core-api/workqueue.rst- Tejun Heo, “Concurrency Managed Workqueue.” 2010
工具
/proc/softirqsmpstatperfsoftirq tracepointsbpftrace
文章导航
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
CPU 调度:CFS 的虚拟运行时间与 EEVDF 的虚拟截止期
从比例份额调度的论文谱系出发,对照 Linux v6.6 与 v6.12 fair.c 源码,解释 CFS 的 vruntime、EEVDF 的 lag/eligible/deadline,并用确定性模拟复现延迟与 lag 的差异。
I/O 调度:在寻道、队列深度与公平性之间取舍
从电梯算法到 blk-mq,解释 Linux I/O 调度器为何在 HDD 上排序、在共享设备上保公平、在 NVMe 上常选择 none,并用确定性模拟展示寻道距离与队列尾延迟的取舍。
用户态内存分配器:size class、线程缓存与碎片边界
从 Wilson 分配器综述到 jemalloc、gperftools tcmalloc 与 mimalloc 的源码路径,解释 size class、线程缓存、跨线程释放和 RSS 碎片;用可复现实验说明不能只凭 allocator 名字下结论。
伙伴系统与 SLUB:Linux 物理页和小对象分配的边界
以 Linux v6.6 源码和确定性模拟为准,解释伙伴系统的 split/coalesce、migratetype 反碎片策略,以及 SLUB 的 per-CPU freelist、partial list 与安全加固。