硬件中断通常都需要在最短的时间内执行完毕,如果将所有硬件中断相关的处理都放在硬件中断处理程序中,那么就达不到这个目的。
通过linux提供的软中断和tasklet,可以将硬件中断处理程序中可以延迟处理的部分放到软中断和tasklet中处理。
1,软中断
linux内核定义了软中断的主要数据结构softirg_vec数组,该数组包含类型为softirg_action的32个元素,即0-31,这也代表软中断的优先级。softirg_action数组包含两个字段:一个函数指针,一个参数指针。通过这两个字段,可以执行任何函数。
在linux2.6中,只有前六个元素被使用。 如下图。在硬件中断中,可以将可延迟的部分以softirg_action的形式插入到软中断的某个优先级中,然后在硬件中断处理完以后,就会触 发软件中断的执行,执行时机在下面会有详细描述。
二,tasklet
tasklet是在软中断之上实现,在实现上做了一些优化,它与软中断的区别:
1,软中断 ,即使是同一类型的软中断,可以并发的运行在多个CPU上,所以内核程序员需要考虑同步问题。
2,类型不同的tasklet可以在多个CPU中并发执行,但相同类型的tasklet就不行,因此用tasklet,不用考虑同步问题,简化了设备驱动程序开发者的工作。
tasklet建立在HI_SOFTIRQ和TASKLET_SOFTIRQ两个软中断之上,分别称为:tasklet和高优先级的tasklet。因为HI_SOFTIRG比TASKLET_SOFTIRQ优先级高,所以后者总比前者先执行,除此之外,没有其它区别。
tasklet和高优先级的tasklet分别存放在tasklet_vec和tasklet_hi_vec数组中。以tasklet_vec为例,假如有8个CPU,那么它包含8个tasklet_head元素,每个元素都指向一个tasklet_struct组成的链表。tasklet_struct包含了一个tasklet所有的信息。
三,软中断和tasklet执行时机
直接贴图,如下。
为了保持软中断的低延迟性,软中断处理程序会一直运行把所有的挂起的软中断处理完。但这样会有一个问题就是它会运行很长时间,因而大大延迟用户态进程的执行。
因此,处理程序只做固定次数的循环,就返回。如果没有处理完,就启用一个内核线程来处理它们。
内核线程为重要而难以平衡的问题提供了解决方案,问题如下:
1,如果软中断过多,如果一直处理软中断,就会造成用户进程的饥渴。
2,如果人为的忽略部分软中断,在下一个执行周期执行它们,在最坏的情况下需要等待一个时钟中断(1/100秒)到来才能执行到,这对于网络开发者来说是不可以接受的。
内核线程优先级较低,因此用户进程就有机会运行,在机器空闲时,挂起的软中断就很快会被被执行。
四,内核线程
不多说,直接上图。下面这段话,说得很明白,感觉就是一个普通的进程,只不过它只运行在内核态罢了。
五,工作队列
在linux2.6中引入了工作队列,一句话,它其实就是用上面的内核线程来实现,只不过上面的内核线程只是在软中断处理不过来时的一种替代方案,而工作队则是完全由内核线程来实现。程序员只需要将执行任务放在工作队列中,然后就会有内核线程来执行它们。
为什么使用工作队列呢?因为有些可阻塞函数,例如访问磁盘,不能在软中断中执行。而工作队列执行在内核线程的上下文中,所以是可以执行这些阻塞函数的。
上面内容可以总结为一句话:内核提供了很多任务执行机制,程序员只需要选择一个合适的机制,将任务通过API放到里面,内核就会达到程序员的目的。