为了让内核能够快速响应硬件设备的消息,引入了中断机制。当硬件设备需要被处理(比如网卡收到了包),会发送一个异步的中断请求给处理器,然后处理器陷入中断上下文中,停止当前执行的程序,转而执行预先设定好的中断处理程序。完成中断处理程序之后给硬件设备一个信号再返回原先的工作继续。
在这套机制中,中断处理程序的编写非常关键。一方面,它必须完成对硬件中断响应的处理,比如网卡中断,要把缓冲区里的内容复制到内存,并且进行解析,然后返回给硬件,告诉硬件前一个中断已经处理掉了。在这个过程中,处理器陷入中断上下文,代码不可阻塞。另一方面,因为不可阻塞,中断响应程序必须尽可能短,以免错过接下来的中断。这样,既要处理的快,又要处理得多,就存在着速度与工作量的矛盾。
对此,解决方案是把中断处理程序拆分成两部分,上半部分和下半部分。上半部分负责处理紧急而简单的任务,例如告诉硬件中断已经处理了;下半部分负责处理与当前中断 相关的、繁重的、不是那么紧急的任务,比如对网卡收到的信息进行解析。上半部分要求接到中断立即执行,越快越好;而下半部分则不必立即执行,在系统比较空闲的时候(不一定是完全空闲,可以加入调度队列参与调度)再执行。
对于上半部分的实现,与嵌入式的中断机制类似,系统收到中断请求立刻执行。
对于下半部分,linux有几种不同的机制。
- 软中断
- tasklet
- 工作队列
除了这些,还有BH、任务队列机制,早在2.5版本即已剔除。
在这三种方法之中,软中断用的很少,因为对数据安全的要求最高,性能也最高,一般也用不到。tasklet是采用软中断实现的。工作队列跟进程上下文一起被调度,使用起来也最容易。
下半部方法 | 上下文 | 顺序执行保障 |
软中断 | 中断上下文 | 没有 |
tasklet | 中断上下文 | 同类型不能同时执行 |
工作队列 | 进程上下文 | 和进程上下文一起调度 |
由于下半部分调度起来也容易产生同步问题,所以加锁的时候有时要求禁止了抢占还要禁止下半部分。