Linux的中断处理机制 [四] - softirq(1)
tasklet
tasklet虽然名字中含有一个"task",但它跟我们熟知的多线程环境中的那个"task"并没有什么直接的关系。前文介绍的9种softirq中,有两种是和tasklet相关的,一个是HI_SOFTIRQ,一个是TASKLET_SOFTIRQ。
由于在实际场景中TASKLET_SOFTIRQ被使用的更多,接下来就以TASKLET_SOFTIRQ为例来讲解tasklet的具体实现。
tasklet作为bottom half的一种实现,自然也有对应的执行函数。当一个hardirq对应的bottom half被设置为tasklet模式时,hardirq执行完后会触发一个tasklet,而这个tasklet将以tasklet_struct的形式,挂载在由tasklet组成的链表上。在tasklet_struct中,"func"和"data"分别是tasklet的执行函数和传入参数。
struct
TASKLET_SOFTIRQ作为softirq的一种,占据了softirq的pending位图中的一个bit,如果有待处理的tasklet,那么TASKLET_SOFTIRQ在位图中对应的bit将被置为1。不同于softirq是按照pending位图从右到左来顺序执行的,当TASKLET_SOFTIRQ获得执行权时,内核将遍历tasklet_struct链表,依次执行各个tasklet注册的处理函数。
可见,tasklet是基于softirq机制实现的,其执行也是完全融合在softirq机制的整体执行中的。相比起其他普通的softirq,tasklet支持在运行过程中动态挂载,其可扩展性更强,因而在实际应用中,softirq较少被直接使用,大家更倾向于采用tasklet这样更为通用的bottom half机制。
在tasklet的实现中,还有一点和其他的softirq不同,那就是它不支持同一种tasklet(tasklet_struct相同的就是同一种)在不同的CPU上执行,所以一个tasklet在执行前,会首先检测其他CPU上是否有正在执行的同种tasklet,这点和hardirq是一样的(参考这篇文章)。这样虽然并行度有所降低,但可以在一定程度上减少tasklet处理函数实现的复杂度。
workqueue
workqueue是将中断处理中可以延后执行的部分作为任务(work item)放在一个队列(queue)中,由一个或多个单独的内核线程(worker)依次执行。与softirq/tasklet底半部机制不同的是,处理workqueue的worker线程始终运行在进程上下文中,可以睡眠。如果延后执行的部分需要睡眠,那么就只能使用workqueue,而不能使用softirq/tasklet。
其实,workqueue机制不光用于中断底半部的实现,对于一般的小型任务,也可按照这种思路,不用自己起一个线程来处理,而是扔到workqueue中去处理,因此workqueue现在已经成为了内核中一种通用的异步处理手段。所以,关于workqueue的详细介绍就不在本系列文章中展开了,而是放在后续单独的文章中。
中断线程化
本系列前面的文章多次提到了“中断线程化”,这主要是考虑到实时性的要求。如果实时任务不能抢占ISR,那么其执行的延迟(latency)时间将不确定,这不符合实时性的要求。
如果内核在编译的时候配置了CONFIG_PREEMPT选项,那么将支持线程抢占,借此就可以实现中断线程化,线程化的中断处理部分被称为threaded interrupt handler。
上文介绍的__do_softirq()执行次数/时间超过限制后触发ksoftirqd只是中断线程化的一种情况,更通用的是使用这篇文章介绍的request_threaded_irq(),只要没有在flag参数中指定"IRQF_NO_THREAD",那么就将按中断线程化的方式处理。
当使用ps命令查看的时候,格式为"irq/%d-%s"就是这些threaded interrupt handler,其中"d"为irq号,"s"为名称:
这些线程是在setup_irq_thread()中创建的:
__setup_irq
线程的入口函数存放在"new->thread_fn"中,而这个"thread_fn"就是request_threaded_irq()相较于传统的request_irq()新增的那个参数。
workqueue也是在线程中处理的,因而也算是一种广义上的中断线程化技术,但与使用单独的线程来进行处理不同,多个中断的workqueue底半部会共用一个worker线程。按理共用线程可以减少上下文切换的开销,提高效率,那为什么又有单独的这种中断线程化方案呢?
这是因为在传统的workqueue中,任务底半部进入队列后是没有严格的优先级之分的(现在新的workqueue实现主要也就分成了普通优先级和高优先级两类),而threaded interrupt handler可以通过设置处理线程的优先级,更好地保证实时性。
参考:
- 中断延迟处理机制「interrupt delay processing」
- Moving interrupts to threads
原创文章,转载请注明出处。