一、软中断类型
在Linux内核中,中断处理分为上半部(硬中断)和下半部。上半部负责快速响应硬件事件,而下半部用于处理耗时任务,避免阻塞系统。下半部有三种机制:软中断(Softirq)、小任务(Tasklet)和工作队列(Workqueue)。它们的区别如下:
1. 软中断(Softirq)
特点:
不允许睡眠:在中断上下文中执行,不能调用可能引发睡眠的函数(如
kmalloc(GFP_KERNEL))。静态定义:类型在编译时确定(如网络、定时器等),无法动态添加新类型。
可重入与并发:同类型软中断可在多个CPU上并行执行,处理函数需支持可重入,需用锁保护临界区。
示例:
网络数据包处理:网卡硬中断将数据包存入队列后,触发
NET_RX_SOFTIRQ软中断。多个CPU可同时调用处理函数net_rx_action(),并行处理不同数据包。函数内部需使用自旋锁保护共享队列。
2. 小任务(Tasklet)
特点:
不允许睡眠:同样在中断上下文中运行。
动态调度:可在运行时动态注册和调度(如驱动初始化时)。
串行执行:同一Tasklet实例仅在一个CPU上执行,无需处理可重入问题。
示例:
USB设备中断处理:硬中断快速确认中断后,调度Tasklet处理数据传输。例如:
void usb_tasklet_func(unsigned long data) { /* 处理数据 */ } DECLARE_TASKLET(usb_tasklet, usb_tasklet_func, 0); // 硬中断中调用: tasklet_schedule(&usb_tasklet);即使系统有多个CPU,
usb_tasklet同一时刻只在一个CPU上运行。
3. 工作队列(Workqueue)
特点:
允许睡眠:在进程上下文中运行(内核线程),可使用阻塞函数(如
msleep()、mutex_lock())。动态管理:可动态创建队列和任务。
线程执行:任务由内核线程按顺序处理。
示例:
磁盘写入操作:需要休眠等待I/O完成时,使用工作队列:
struct work_struct write_work; void write_work_func(struct work_struct *work) { // 调用可能阻塞的函数,如文件写入 filp->f_op->write(...); } // 初始化并提交任务: INIT_WORK(&write_work, write_work_func); schedule_work(&write_work);工作队列处理函数可安全调用
write()等可能阻塞的操作。
三者的对比总结
特性 软中断 小任务 工作队列 是否允许睡眠 否 否 是 动态添加/删除 否(静态编译时定义) 是 是(可动态创建队列) 并发性 多CPU并行,需可重入 单CPU串行,无需可重入 按线程调度,顺序执行
软中断(softirq)是中断处理程序在开启中断的情况下执行的部分,可以被硬中断抢占。内核定义了一张软中断向量表,每种软中断有一个唯一编号,对应一个softirq_action实例,内核源码如下:


软中断类型内核源码如下:

二、注册软中断以及触发软中断
函数open_softirq()来注册软中断处理函数,在软中断向量表中为指定的软中断编号设置处理函数,内核源码如下:

函数raise_softirq()用来触发软中断,参数是软中断编号:

三、执行软中断
在中断处理程序的后半部分执行软中断,对执行时间有限制:不能超过 2 毫秒,并且最多执行 10 次;每个处理器有一个软中断线程,调度策略是 SCHED_NORMAL,优先级是120。
中断处理程序执行软中断:



软中断线程:

以NIC网卡接收数据触发软中断为例,讲解这些函数工作流程:
1. open_softirq
- 功能:用于注册软中断处理函数。在内核中,软中断类型是预先定义好的 ,如
NET_RX_SOFTIRQ(网卡接收数据软中断类型)。通过open_softirq(net_rx_softirq, net_rx_action),将net_rx_action函数注册为NET_RX_SOFTIRQ软中断的处理函数。这样当该软中断被触发时,内核知道要调用哪个函数来处理。- 流程:将传入的处理函数指针赋值给对应软中断向量表(
softirq_vec数组 )中该软中断类型索引位置的action成员。2. raise_softirq
- 功能:标记要触发的软中断,使其进入待处理状态。当 NIC 网卡接收到数据时,相关代码会调用
raise_softirq来触发NET_RX_SOFTIRQ软中断。- 流程:
- 首先调用
raise_softirq_irqoff,该函数通过__raise_softirq_irqoff设置当前 CPU 的软中断 pending 标志位(对应__softirq_pending中NET_RX_SOFTIRQ相关位) ,表示有软中断等待处理。- 然后检查当前是否不在中断上下文(通过
in_interrupt判断) ,如果满足条件,就唤醒软中断守护进程ksoftirqd(调用wakeup_softirqd),准备处理软中断;若在中断上下文,则软中断会在中断退出阶段处理。3. irq_exit
- 功能:在硬中断处理完成后调用,用于退出中断上下文,并检查是否有需要处理的软中断。当 NIC 网卡硬中断处理完成后,会执行到
irq_exit。- 流程:
- 执行一些统计和追踪相关操作,如
account_system_vtime(current)、trace_hardirq_exit。- 通过
sub_preempt_count(IRQ_EXIT_OFFSET)标识 HARDIRQ 中断上下文结束。- 使用
in_interrupt判断当前是否处于中断上下文,用local_softirq_pending检查是否有等待处理的软中断。若不在中断上下文且有软中断等待处理,则调用invoke_softirq。4. invoke_softirq
- 功能:实际触发软中断处理,它是
__do_softirq的宏定义(#define invoke_softirq() __do_softirq()),负责调用软中断处理函数。- 流程:直接调用
__do_softirq,进入软中断处理核心流程。5. _do_softirq
- 功能:软中断处理核心函数,遍历并执行处于 pending 状态的软中断处理函数。
- 流程:
- 获取当前 CPU 软中断位图
pending = local_softirq_pending(),得到哪些软中断被触发。- 禁止本地 CPU 的软中断(
__local_bh_disable),防止处理过程中软中断被干扰。- 标记进入 softirq context(
lockdep_softirq_enter),记录相关锁依赖信息。- 循环处理位图中的软中断:
- 对位图清零(
set_softirq_pending(0))。- 开启本地 CPU 硬中断(
local_irq_enable),允许硬中断打断软中断处理(但软中断不可嵌套)。- 获取软中断描述(
h = softirq_vec),根据软中断位图找到对应的软中断处理函数并执行(h->action(h)) 。- 处理过程中检查是否超时(通过
jiffies和设定的最大处理时间MAX_SOFTIRQ_TIME比较 )和达到最大重启次数(max_restart,默认 10 次) ,若满足条件且还有软中断未处理完,则唤醒ksoftirqd继续处理剩余软中断。- 处理完成后恢复相关设置,如关闭本地 CPU 硬中断等。
6. run_softirqd
- 功能:
ksoftirqd内核线程执行的函数,当软中断处理在irq_exit中未处理完,或内核其他代码主动触发软中断且不在中断上下文时,ksoftirqd会被唤醒执行run_softirqd来处理软中断。- 流程:在内核关闭抢占的情况下调用
__do_softirq,执行与上述__do_softirq类似的软中断处理流程,遍历并执行等待处理的软中断处理函数。
https://github.com/0voice

![[ComfyUI] 如何升级自定义节点(Custom Nodes)](https://i-blog.csdnimg.cn/direct/ea995efbae97424a9a5206ba44607d24.png)

















