Linux 中断处理的核心:顶半部和底半部

http://blog.csdn.net/yuesichiu/article/details/8286469

http://book.51cto.com/art/201311/418629.htm

设备的中断会打断内核中进程的正常调度和运行,系统对更高吞吐率的追求势必要求中断服务程序尽可能地短小精悍。但是,这个良好的愿望往往与现实并不吻合。在大多数真实的系统中,当中断到来时,要完成的工作往往并不会是短小的,它可能要进行较大量的耗时处理。 

        为了在中断执行时间尽可能短和中断处理需完成大量工作之间找到一个平衡点,Linux 将中断处理程序分解为两个半部:顶半部(top  half)和底半部(bottom half)。 顶半部完成尽可能少的比较紧急的功能,它往往只是简单地读取寄存器中的中断状态并清除中断标志后就进行“登记中断”的工作。“登记中断”意味着将底半部处理程序挂到该设备的底半部执行队列中去。这样,顶半部执行的速度就会很快,可以服务更多的中断请求。现在,中断处理工作的重心就落在了底半部的头上,它来完成中断事件的绝大多数任务。底半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断,这也是底半部和顶半部的最大不同,因为顶半部往往被设计成不可中断。底半部则相对来说并不是非常紧急的,而且相对比较耗时,不在硬件中断服务程序中执行。

顶半部是实际响应中断的过程,也就是用request_irq注册的中断例程。而底半部会在稍后比较安全的时间内执行的过程。即底半部的中断都是打开的。比较典型的情况是顶半部保存设备的数据到一个设备特定的缓存区并调度它的底半部,然后退出;这个过程是非常迅速的。然后底半部执行其他必要的工作,例如唤醒进程、启动另外的I/O操作等等。这种方法允许在底半部工作期间,顶半部还可以继续为新的中断服务。

      尽管顶半部、底半部的结合能够改善系统的响应能力,但是,僵化地认为 Linux设备驱动中的中断处理一定要分两个半部则是不对的。如果中断要处理的工作本身很少,则完全可以直接在顶半部全部完成。其他操作系统中对中断的处理也采用了类似于Linux系统的方法,真正的硬件中断服务程序都应该尽可能短。因此,许多操作系统都提供了中断上下文和非中断上下文相结合的机制,将中断的耗时工作保留到非中断上下文去执行。 

        Linux 系统实现底半部的机制主要有tasklet,工作队列和软中断。Linux 的中断处理分为两个半部,顶半部处理紧急的硬件操作,底半部处理不紧急的耗时操作。tasklet 和工作队列都是调度中断底半部的良好机制,tasklet 基于软中断实现。内核定时器也依靠软中断实现。内核中的延时是忙等待或者睡眠等待,为了充分利用CPU资源,使系统有更好的吞吐性能,在对延迟时间的要求并不是很精确的情况下,睡眠等待通常是值得推荐的。

1) tasklet (首选机制),它非常快但是所有的 tasklet 代码必须是原子的;始终工作在中断期间运行。

2)工作队列它可能有更高的延时,但允许休眠。工作在一个特殊内核进程的上下文中运行。




14.3  Linux 中断处理的核心:顶半部和底半部

中国有句俗话:鱼与熊掌不可兼得。这句话也充分体现在了中断处理上。在一定的时间内完成的工作量和工作的复杂程度往往是对立的。也就是说,如果想在1小时内做很多工作,那么每项工作就不能太复杂,否则不可能完成任务。中断处理也是一样。内核在处理中断请求时要求在单位时间内处理尽可能多的中断,也就是说要求处理中断的吞吐率要尽可能地大。这就要求中断处理函数中的代码尽可能地短小,而且不能有耗时的操作。但这往往很不现实。事实上,大多数中断处理程序是很复杂的,也不是在很短的时间就可以执行完的。为了解决这个问题,内核设计者将中断处理分为两个阶段,也就是我们常说的顶半部(Top Half)和底半部(Bottom Half)。通过顶半部和底半部,可以在某种程度上缓解鱼与熊掌不可兼得的问题。

处理中断可以分为如下两部分。

接收和响应中断请求。

处理中断的业务逻辑。

一般复杂的工作都在处理中断的业务逻辑中。而接收和响应中断请求会在很短的时间被处理完。根据这两项工作所需的时间差异,很容易想到可以同步来执行中断请求的接收和响应,而通过异步的方式执行耗时比较多的操作,也就是处理中断的业务逻辑。

我们在编写服务端网络程序时往往会开启一个监听线程来接收客户端的请求。一旦接收到某个客户端的请求,一般不会直接在监听线程中处理客户端的请求,而是再开启一个专门处理客户端请求的线程,并在该线程中处理客户端的请求。这样监听线程就可以解脱出来处理更多的客户端请求,从而大大提高服务端程序的吞吐量。

中断处理程序和服务端网络程序类似,当硬件向内核发送中断请求时,内核(在这里内核就相当于服务端网络程序)首先会接收中断请求,这个接收中断请求的任务就是由中断处理程序的顶半部完成的。然而在顶半部中并不会执行中断处理的核心代码,而这些代码需要在底半部完成。对于顶半部来说,除了接收中断请求外,还会进行"登记工作"。也就是说要将底半部处理程序挂到发送中断请求的设备的底半部执行队列中。这样的安排,顶半部的执行速度就会很快,可以服务更多的中断请求。

现在,中断处理工作的重心已经落在了底半部的头上,由底半部来完成中断处理的大部分工作。底半部几乎做了中断处理程序所有的事情,而且可以被新的中断打断,这也是底半部和顶半部的最大不同,因为顶半部不能被其他中断打断。底半部则相对来说并不是非常紧急的,而且相对比较耗时,并且不在硬件中断服务程序中执行。

尽管中断处理可以通过顶半部和底半部的结合来改善系统的响应能力,但是在实际的应用中并不一定要分两个半部来处理。如果处理中断的任务很小,耗时比较短,完全可以直接在顶半部完成,根本就不需要底半部的参与。



展开阅读全文

没有更多推荐了,返回首页