硬中断里只做「应答 + 调度下半部」,重活放到 softirq / tasklet / workqueue 。若把耗时逻辑塞进 hardirq,会出现关抢占过久、延迟尖刺、甚至 soft lockup。本文沿 irq_exit() → __do_softirq() 说明下半部何时跑、和 NAPI/定时器的关系,并给出可执行的绑核与观测手段。
源码锚点
| 路径 | 作用 |
|---|---|
kernel/softirq.c |
__do_softirq()、irq_exit 触发、open_softirq |
include/linux/interrupt.h |
request_irq、tasklet、softirq 向量声明 |
kernel/irq/manage.c |
IRQ 申请与线程化中断 |
net/core/dev.c |
NET_RX_SOFTIRQ、NAPI 轮询 |
kernel/time/timer.c |
TIMER_SOFTIRQ |
Documentation/core-api/irq/irq-domain.rst 等 |
中断子系统文档 |
软中断执行核心(概念):
c
/* kernel/softirq.c */
asmlinkage __visible void __softirq_entry __do_softirq(void)
{
unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
/* 按 pending mask 依次调用 softirq_vec[nr].action() */
}
注册与触发:
c
open_softirq(NET_RX_SOFTIRQ, net_rx_action);
raise_softirq(NET_RX_SOFTIRQ); /* 或 raise_softirq_irqoff */
调用链
text
硬件 IRQ
→ 架构入口 → handle_irq_event
→ action->handler() /* hardirq:短、不可睡 */
→ 常 raise_softirq / napi_schedule
→ irq_exit()
→ 若不在中断嵌套且有 pending softirq
→ invoke_softirq
→ __do_softirq()
→ softirq_vec[i].action()
├─ NET_RX_SOFTIRQ → net_rx_action → NAPI poll
├─ TIMER_SOFTIRQ → run_timer_softirq
├─ TASKLET_SOFTIRQ → tasklet_action
└─ ...
若 softirq 跑太久 / ksoftirqd 接管:
→ wake ksoftirqd/<cpu>
驱动常用分层:
text
request_irq(handler)
handler: 清状态、读关键寄存器、napi_schedule / tasklet_schedule
下半部: 组包、协议栈、耗时处理
更重或可能睡眠: queue_work(system_wq, ...)
重点知识
1. 为何要下半部
hardirq 期间通常关(或延迟)同线中断,逻辑越长,同设备与同 CPU 延迟越大。softirq 仍在软中断上下文(不可睡眠),但可批量、可延期到 ksoftirqd。
2. softirq vs tasklet vs workqueue
| 机制 | 可睡眠 | 典型用途 |
|---|---|---|
| softirq | 否 | 网络 RX/TX、timer、block 等高频 |
| tasklet | 否 | 驱动轻量延后(同 CPU 串行化语义) |
| workqueue | 是 | 可能阻塞的收尾、与用户态交互 |
新代码更常见 threaded IRQ (request_threaded_irq)或 NAPI,而不是手写 tasklet。
3. 中断亲和与多队列
bash
# IRQ 绑核
cat /proc/interrupts
echo <mask> > /proc/irq/<n>/smp_affinity
# 网卡多队列 + XPS/RPS(按需)
ls /sys/class/net/eth0/queues/
IRQ 全堆 CPU0 时,同核 softirq 也会挤在一起,表现为单核 100%、其它核空闲。
4. 时间与观测
bash
cat /proc/softirqs
watch -n1 cat /proc/softirqs
perf top -e irq:softirq_entry
# 延迟
cyclictest -p 90 -i 1000 -l 10000 # 实时评估环境
5. 设计意图与踩坑
- 意图:硬中断极短,吞吐型工作在 softirq/NAPI 批处理。
- 坑 :在 hardirq 里
mutex_lock/ 打印刷屏。 - 坑:softirq 里睡------直接 BUG。
- 坑 :忽略
ksoftirqd占满,只盯硬中断计数。 - 坑 :时间相关逻辑用可回拨的 wall clock;超时用
CLOCK_MONOTONIC。
Checklist
- hardirq handler 只做应答与调度下半部,无睡眠 API
- 能指出
irq_exit→__do_softirq的触发条件 - 按场景选 softirq / threaded IRQ / workqueue
- 用
/proc/interrupts与/proc/softirqs确认未单核打满 - 高 PPS 网卡检查 NAPI 与中断聚合(coalescing)
- 延迟敏感线程验证未与重 softirq 同核恶性争抢