中断与下半部机制
本文对应 成长路径 高优先级:在驱动中 缩短硬中断,按场景选择 tasklet / workqueue / threaded IRQ。
学习目标
- 区分 上半部(硬中断) 与 下半部 的职责。
- 使用选用表决定 tasklet / workqueue / kthread。
- 能阅读
/proc/interrupts与 IRQ affinity。
选用表
| 机制 | 上下文 | 可睡眠 | 典型场景 |
|---|---|---|---|
| 硬中断 top half | 中断 | 否 | 清中断源、读 FIFO 指针、调度下半部 |
| tasklet | 软中断 | 否 | 短、非重入敏感逻辑(旧代码常见) |
| workqueue | 进程 | 是 | I2C 传输、分配内存、用户通知 |
| threaded IRQ | 内核线程 | 是 | 新驱动推荐,上半部极短 |
| kthread | 进程 | 是 | 轮询、复杂状态机 |
原则:硬中断里只做必须快的;其余延后。
硬中断示例(概念)
static irqreturn_t my_irq(int irq, void *dev_id)
{
/* 读状态寄存器、ack 中断 */
tasklet_schedule(&priv->t);
return IRQ_HANDLED;
}避免:printk、mutex_lock、mdelay。
tasklet
static void my_tasklet(struct tasklet_struct *t)
{
struct my_dev *priv = from_tasklet(priv, t, t);
/* 仍不可睡眠 */
}同一 tasklet 不会并行 执行,但可与其他 tasklet 并发。
workqueue
schedule_work(&priv->work);
/* work_fn 中可 mutex、kmalloc(GFP_KERNEL) */系统 workqueue 与 WQ_MEM_RECLAIM 等专用队列按负载选择。
threaded IRQ(推荐)
request_threaded_irq(irq, hard_handler, thread_fn, flags, name, dev);- hard_handler:
IRQ_WAKE_THREAD唤醒线程。 - thread_fn:可睡眠,适合 SPI/I2C 等慢总线。
与同步的关系
硬中断与进程上下文争用同一数据时,需 spinlock_irqsave 或 RCU,见 内核同步机制总览。
实践清单
- 统计某网卡 IRQ 在
/proc/interrupts的增长速率 - 将 demo 驱动中的
mdelay从中断处理移到 workqueue - 对比 threaded IRQ 与 tasklet 的 latency(示波器或
trace_irqsoff)