软中断
软中断的定义
中断实际分为两个部分,硬中断
和软中断
。由于处理器要及时的响应处理外设的中断请求,但是外设中断请求可能需要消耗一定的时间才能完成,所以不能处理器一直工作中断函数中,这时就可以以软件的方式模拟一个中断,继续处理未完成的工作,这就是软中断。软中断用于处理费时费力的任务。
软中断的触发
由于软中断本身就为异步执行设计的,所以可以在其他很多地方使用,不仅是只在硬中断后续处理外设中断任务中使用。以下的地方会触发软中断:
- 在硬中断处理完中断注册服务后(
irq_exit()
)检查并触发。 - 在任务调度(
schedule()
)中触发。 - 在系统调用(
syscall()
)时触发。
对于每个CPU,都有一个irq_cpustat_t
的数据结构,里面有一个__softirq_pending
(暂时叫软中断向量)变量,用于表示该CPU的哪个软中断处于挂起状态。软中断在触发后(设置__softirq_pending对应位的值)可以立马被执行,也可能会通过wakeup_softirqd()
通知一个名为软中断服务deamon内核子线程中执行。
软中断的分类
与硬中断不同的是,软中断可以由内核代码自定义其数量,而硬中断数量与中断控制器硬件相关,是一种宝贵的硬件资源。因为是软件定义的,就可以有优先级之分,其类别差异就是优先级差异,软中断执行时,会优先执行高优先级的软中断服务,如果时间或次数运行,在执行较低优先级的软中断服务。重要的软中断有:
- HI_SOFTIRQ,高优先级tasklet,优先级最高
- TASKLET_SOFTIRQ, 常规tasklet
他们被用于tasklet服务,由于软中断数量在编译内核后是固定的,所以大多时候我们通过在tasklet中注册服务来实现软中断服务。
软中断描述符表
软中断描述符表是一个静态数组,保存着已注册软中断服务的函数地址和数据。当通过open_softirq()
注册了一种软中断在软中断描述表相应的位置上保存注册的服务,与软中断向量形成映射,当软中断触发、执行时判断该调用那个软中断服务的函数。
软中断执行流程
在每个CPU上一次软中断处理的一个典型流程是:
- (硬中断执行完毕,)开中断。
- 检查该CPU是否处于嵌套中断的情况,如果处于嵌套中,则不执行软中断,也就是在最外层中断才执行软中断。
- 执行软中断,设置一个软中断执行最多使用时间和循环次数。
- 进入循环,获取CPU的
__softirq_pending
的副本。 - 执行此
__softirq_pending
副本中所有需要执行的软中断。 - 如果软中断执行完毕,退出中断上下文。
- 如果还有软中断需要执行(在软中断期间又发发生了中断,产生了新的软中断,新的软中断记录在CPU的
__softirq_pending
上,而我们的__softirq_pending
只是个副本)。 - 检查此次软中断总共使用的时间和循环次数,条件允许继续执行软中断,循环次数减一,并跳转到第4步。
tasklet服务
该服务被用于需要软中断特性的服务的延迟执行, tasklet有两个优先级,分别依靠HI_SOFTIRQ(高优先级)和TASKLET_SOFTIRQ(低优先级)软中断实现。每一个注册的tasklet服务以链表的方式组织起来,在一定时间和次数内被一次调用执行。虽然软注册同一个tasklet任务的中断服务可能在不同的CPU上触发,单由于tasklet服务的状态控制和实现的关系,tasklet服务一定不会并发的在不同的CPU上执行,所以可以不必是可重入的。
软中断守护处理线程
由于一次软中断服务响应也有时间和调用次数限制,为了得到更好的响应,把剩下的需要响应的软中断服务交给一个内核子线程处理,该子线程就是软中断守护处理线程,它仅仅是无限的检查是否有需要执行的软中断服务,然后执行。