Linux 中断分 top-half(硬中断)和 bottom-half(软中断/tasklet/workqueue):硬中断里只做最紧急的事(读数据、清标志),耗时处理推迟到底半部;共享中断要用 IRQF_SHARED 并判断是否是自己的设备。
本文概述
中断是驱动开发核心,面试常考:request_irq、顶半部/底半部、tasklet/workqueue/线程化中断、共享中断。回答要讲清为什么要分两半。
核心要点
request_irq() 注册中断:传中断号、处理函数、flags、设备名、dev_id
硬中断(top-half):关中断上下文,不能睡眠、不能调可能睡眠的函数
底半部(bottom-half):软中断、tasklet(原子)、workqueue(可睡眠)
中断里做最少:读数据、清标志;复杂处理放 workqueue
共享中断用 IRQF_SHARED,处理函数通过 dev_id 判断是不是自己的中断

面试官考察点
面试官看你是否写过驱动中断、是否理解上下文。重点听:硬中断限制、为什么要分、三种底半部区别。
回答思路
先讲硬中断的限制,再讲为什么要分两半,最后讲三种底半部怎么选。
参考回答
为什么要分两半。硬中断运行时本地中断关闭(至少当前 CPU),这段时间越长,系统响应越慢。所以硬中断里只能做最紧急的事:
读硬件数据(如 SPI 读 FIFO);
清中断标志;
告诉底半部有事件要处理。
复杂处理(协议解析、上报用户态)放底半部,不阻塞其他中断。
三种底半部对比。
| 机制 | 上下文 | 能否睡眠 | 适用 |
|---|---|---|---|
| 软中断/ tasklet | 原子 | 不能 | 短、快、不睡眠 |
| workqueue | 进程 | 能 | 耗时、要拿 mutex |
| 线程化中断 threaded_irq | 进程 | 能 | 简单驱动,直接用线程 |
典型写法。
// 中断处理函数(硬中断)
static irqreturn_t my_irq_handler(int irq, void *dev_id) {
struct my_dev *dev = dev_id;
if (!my_irq_is_mine(dev)) return IRQ_NONE;
my_clear_irq(dev);
schedule_work(&dev->work); // 底半部
return IRQ_HANDLED;
}
// 初始化
request_irq(irq_num, my_irq_handler, IRQF_TRIGGER_FALLING,
"mydev", dev);
INIT_WORK(&dev->work, my_work_func);
硬中断里不能做什么。
不能调用 copy_to_user(可能睡眠);
不能拿 mutex(会睡眠);
不能 printf(虽然 printk 不睡眠但耗时);
不能做长循环或延时。
共享中断。多个设备共用一根中断线时,用 IRQF_SHARED 注册,处理函数要先读硬件状态寄存器判断是不是自己的设备触发的,不是就返回 IRQ_NONE。

扫码关注




























