为什么重新new两个线程线程号相同_Linux中的中断处理机制 [六] - 从tasklet到中断线程化...

f77c348969587e305a762494274661d7.png

Linux的中断处理机制 [四] - softirq(1)

tasklet

tasklet虽然名字中含有一个"task",但它跟我们熟知的多线程环境中的那个"task"并没有什么直接的关系。前文介绍的9种softirq中,有两种是和tasklet相关的,一个是HI_SOFTIRQ,一个是TASKLET_SOFTIRQ。

fe3e8781f9dec32bf6d946823764a774.png

由于在实际场景中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注册的处理函数。

4242bd8d8f11dc194217dfb6eec322da.png
本图出处请见文末参考链接

可见,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。

47164c9b8e12bf091ddccf3ee36a0a3b.png

其实,workqueue机制不光用于中断底半部的实现,对于一般的小型任务,也可按照这种思路,不用自己起一个线程来处理,而是扔到workqueue中去处理,因此workqueue现在已经成为了内核中一种通用的异步处理手段。所以,关于workqueue的详细介绍就不在本系列文章中展开了,而是放在后续单独的文章中。

中断线程化

本系列前面的文章多次提到了“中断线程化”,这主要是考虑到实时性的要求。如果实时任务不能抢占ISR,那么其执行的延迟(latency)时间将不确定,这不符合实时性的要求。

f9db72c56f1ee446be60fb1e510d6ed3.png

如果内核在编译的时候配置了CONFIG_PREEMPT选项,那么将支持线程抢占,借此就可以实现中断线程化,线程化的中断处理部分被称为threaded interrupt handler。

68697c99958ceacda6c3a2f2bbcca82e.png

上文介绍的__do_softirq()执行次数/时间超过限制后触发ksoftirqd只是中断线程化的一种情况,更通用的是使用这篇文章介绍的request_threaded_irq(),只要没有在flag参数中指定"IRQF_NO_THREAD",那么就将按中断线程化的方式处理。

当使用ps命令查看的时候,格式为"irq/%d-%s"就是这些threaded interrupt handler,其中"d"为irq号,"s"为名称:

55c4cdc1ee4346baf1855a72a55b0072.png

这些线程是在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

原创文章,转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值