------AI创作------
一、核心背景:中断处理的矛盾
硬件中断触发时,CPU 立刻停下当前程序去处理设备,但有两个约束:
- 中断上下文环境限制:不能睡眠、不能阻塞、不能长时间循环;
- 中断屏蔽:处理当前中断时,同级别硬件中断会被屏蔽,处理越久,其他硬件越容易丢失中断。
解决方案:拆分工作,分成两段执行
- 上半部:紧急、必须立刻硬件应答的少量工作(原子、快、禁止睡眠)
- 下半部:耗时、复杂、可以延后处理的工作(允许调度、耗时操作)
二、上半部 Top Half(中断服务程序 ISR)
职责(只做必须马上干的事)
- 读取硬件寄存器,清除中断标志(否则硬件会反复发中断);
特点
- 运行在中断上下文,无进程、不能
sleep()、不能互斥锁睡眠;
irqreturn_tdev_isr(int irq, void *dev_id){ // 1. 清硬件中断标志 read_reg(); // 2. 拷贝少量硬件数据 // 3. 触发下半部延后处理 tasklet_schedule(&dev->tasklet); return IRQ_HANDLED;}
三、下半部 Bottom Half(延后执行)
职责(所有耗时工作丢这里)
数据包解析、内存拷贝、协议处理、日志上报、大量循环、加锁等待等;
特点
- 依然不属于普通进程上下文,但允许调度让出 CPU,不会长时间阻塞硬件中断;
四、Linux 四种下半部实现机制(演进顺序)
1. tasklet(最常用,简单设备驱动首选)
- 基于软中断,同一种 tasklet 不会并行执行;
- API:
tasklet_init / tasklet_schedule
2. 软中断 softirq(底层、高性能)
- 仅内核网络、块设备子系统使用,驱动开发者很少直接用;例:网络收包、磁盘 IO 完成处理。
3. workqueue 工作队列(唯一能睡眠的下半部)
- 中断里有耗时阻塞操作必须用 workqueue;API:
INIT_WORK / schedule_work
4. 旧机制:bh(废弃,2.6 内核后彻底移除)
早期下半部,性能差,现已淘汰。
五、举个通俗例子:网卡中断
上半部(ISR,立刻执行)
- 触发软中断(下半部);耗时几微秒,快速退出,放开硬件中断
下半部(softirq 软中断)
- 把数据递交给用户态 socket;耗时更长,但不会阻塞其他网卡中断
| | |
|---|
| | |
| | workqueue 可以 sleep,tasklet/softirq 不行 |
| | |
| | |
| | |
七、设计原则(写驱动必遵守)
- ISR(上半部)代码越少越好,绝不放循环、打印、复杂运算;
- 凡是耗时操作、内存分配、文件操作、阻塞等待,全部扔下半部;
- 需要睡眠 → workqueue;简单快速处理 → tasklet;高速吞吐设备 → softirq。
八、一句话总结
上半部快速响应硬件,避免丢中断;下半部延后处理脏活累活,保证系统响应速度。