Linux 内核中断下半部与工作队列深度实战:从硬中断到线程化执行的完整生产路径
在 Linux 内核编程中,中断处理的第一原则是"硬中断只做最少的事"。任何可能阻塞、耗时或需要进程上下文的操作都必须推迟到中断下半部(bottom half)执行。本文将从硬件中断触发开始,完整剖析 softirq、tasklet、threaded IRQ 与 workqueue 四种下半部机制的底层实现、性能权衡与生产级部署策略,并给出中断负载的可观测性方案。
1. 中断处理的基本时空约束
当硬件外设触发中断时,CPU 通过 IDT(中断描述符表)跳转到对应的 irq_handler_t 函数。这个执行上下文被称为"硬中断上下文"(hardirq context),它有以下严格约束:
- 非抢占性:触发时自动关闭本地 CPU 中断(IRQ_DISABLED),直到返回
- 无进程上下文:current 指向被中断的进程,但不能调用任何可能睡眠的函数
- 栈空间有限:x86 上硬中断栈仅 16KB(thread_info 中断栈共享一页)
- 执行时间敏感:长时间关中断会导致中断丢失(看门狗触发 NMI 检测 hardlockup)
因此内核设计遵循"顶半部/底半部"(top/bottom half)分离原则:顶半部只做状态读取、ACK 应答和调度底半部返回;真正的数据处理逻辑在底半部执行。
2. Softirq:内核底半部的基石
softirq 是 Linux 最高频、延迟最低的底半部机制。内核预定义了以下几种软中断(定义于 include/linux/interrupt.h):
enum {\n HI_SOFTIRQ = 0,\n TIMER_SOFTIRQ,\n NET_TX_SOFTIRQ,\n NET_RX_SOFTIRQ,\n BLOCK_SOFTIRQ,\n IRQ_POLL_SOFTIRQ,\n TASKLET_SOFTIRQ,\n SCHED_SOFTIRQ,\n HRTIMER_SOFTIRQ,\n RCU_SOFTIRQ,\n};调度时机(三处触发点):
- irq_exit():硬中断返回时,检查是否有待处理 softirq
- ksoftirqd:当软中断被唤醒但已在上半上下文执行时,唤醒 per-CPU 内核线程处理
- local_bh_enable():显式开启 bottom half 软中断时检查待处理
关键设计约束:softirq 可以在多个 CPU 上并发执行(reentrant),但同一编号的 softirq 在同一个 CPU 上是串行不重入的。
3. Tasklet:基于 softirq 的串行化封装
tasklet 基于 softirq 实现,解决了两个关键问题:
- 串行化保证:同一 tasklet 不会在多个 CPU 上同时执行
- 动态注册:模块可以在运行时动态创建 tasklet
4. 工作队列(Workqueue):可睡眠的进程上下文执行
workqueue 是唯一可以在中断底半部中调用可能睡眠、内存分配(GFP_KERNEL)、互斥锁、用户态拷贝等操作的机制。其本质是一组内核工作线程(worker thread)的线程池。
4.1 核心数据结构
struct work_struct {\n atomic_long_t data;\n struct list_head entry;\n work_func_t func;\n};\n\nstruct worker_pool {\n spinlock_t lock;\n struct list_head worklist;\n struct list_head workers;\n struct timer_node *idle_timer;\n unsigned int flags;\n int nr_idle;\n};4.2 API 层次
级别一:预定义工作队列
schedule_work(

发表评论 取消回复