一、中断下半部类型及优先级(从高到低)
中断下半部共分三大类,优先级严格递减,且软中断内部有内核固定优先级分级,具体划分如下:
1. 软中断(softirq)
下半部中最高优先级,运行在中断上下文,不可休眠、执行速度快,内核静态定义固定类型,内部优先级从高到低依次为:
HI_SOFTIRQ(高优先级tasklet)→ TIMER_SOFTIRQ(时钟软中断)→ NET_TX_SOFTIRQ(网络发送)→ NET_RX_SOFTIRQ(网络接收)→ BLOCK_SOFTIRQ(块设备)→ IRQ_POLL_SOLLIRQ → TASKLET_SOFTIRQ(普通tasklet)→ SCHED_SOFTIRQ(进程调度)→ HRTIMER_SOFTIRQ(高精度定时器)→ RCU_SOFTIRQ(RCU资源回收)
2. tasklet
基于软中断实现,依托 HI_SOFTIRQ 和 TASKLET_SOFTIRQ 两种软中断,优先级与所属软中断完全一致;同样运行在中断上下文,不可休眠,使用比软中断更简单,支持动态注册,是驱动开发常用下半部方案。
3. 工作队列(workqueue)
下半部最低优先级,运行在内核线程上下文,允许休眠(可调用mutex、内存分配等阻塞接口),适合处理耗时、可阻塞的下半部逻辑,对系统实时性影响最小。
二、指定中断下半部类型的内核调用接口
在驱动开发中,硬件中断上半部处理完成后,可通过对应内核API,指定该中断的下半部为软中断/tasklet/工作队列,具体接口及使用逻辑如下:
(一)指定下半部为软中断(softirq)
软中断为内核静态分配,驱动极少自定义,多用于网络、块设备等核心模块,核心接口:
open_softirq(int nr,
void (*action)(struct softirq_action *))
- 作用:注册指定类型的软中断处理函数
- 参数: nr 为软中断类型(如NET_RX_SOFTIRQ), action 为下半部处理函数
raise_softirq(int nr)
- 作用:中断上半部中调用,触发对应软中断,标记其挂起待执行
- 示例:上半部执行完,调用 raise_softirq(NET_RX_SOFTIRQ) ,指定网卡中断下半部为网络接收软中断
(二)指定下半部为tasklet
驱动开发最常用,接口简单、无需关注软中断底层,核心接口:
DECLARE_TASKLET(name, func, data)
- 作用:静态定义tasklet结构体,绑定处理函数
tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)
- 作用:动态初始化tasklet
tasklet_schedule(struct tasklet_struct *t)
- 作用:中断上半部中调用,调度tasklet执行,将其加入对应软中断链表
- 示例:上半部硬件处理完成后,调用 tasklet_schedule(&my_tasklet) ,指定该中断下半部为tasklet
(三)指定下半部为工作队列(workqueue)
适合可休眠、耗时场景,分共享工作队列和自定义工作队列,核心接口:
1. 共享工作队列(简单易用,无需创建队列)
DECLARE_WORK(name, func) :静态定义工作项
INIT_WORK(struct work_struct *work, void (*func)(struct work_struct *work)) :动态初始化工作项
schedule_work(struct work_struct *work) :中断上半部中调用,调度工作项到共享队列执行
2. 自定义工作队列(避免共享队列阻塞)
create_workqueue(const char *name) :创建自定义工作队列
queue_work(struct workqueue_struct *wq, struct work_struct *work) :调度工作项到自定义队列
- 示例:上半部完成后,调用 schedule_work(&my_work) ,指定中断下半部为工作队列
三、关键补充规则
1. 所有下半部必须等所有中断上半部(含嵌套的高优先级上半部)执行完毕,才会按优先级调度执行;
2. 软中断、tasklet不可休眠,工作队列可休眠;
3. 下半部执行时可被新的硬件中断(上半部)打断,不会互相抢占;
4. 同一优先级软中断/tasklet,会遍历链表执行完所有挂起任务,才会退出下半部流程。
关注我,每天进步积累一点点,谢谢您!