1.概述
通过前文,我们已经知道了中断通常由上下两部分组成。在上部分,也就是中断处理程序,完成中断请求的响应以及完成那些对时间要求紧迫的工作;而在下部分,通常完成那些被推后的工作,因为这部分工作对时间的要求相对宽松一些。通过了解上下两部分的工作情况,可以更好的理解中断这个概念。从下半部分执行机制来看——不管是tasklet还是工作队列——这些推后的工作总是在上半部分被调用,然后交给内核在适当的时间来完成。那么,中断上部分具体是如何工作的?内核对中断是如何处理的?
在开始分析之前,需要说明的是接下来分析的属于内核对外设产生的中断的处理情况,异常的处理过程在本文的最后会有简单解释。
所有的中断的处理程序在init_IRQ函数中都被初始化为interrupt[i]。interrupt数组中每一项均指向一个代码片段:
该代码片段除了将中断向量号压入堆栈,还会跳到一个公共处理程序common_interrup:
3 |
864
addl $-0x80,(%esp) /* Adjust vector into the [-256,-1] range * / |
这段公共处理程序会将中断发生前的所有寄存器的值压入堆栈,也就是保存被中断任务的现场。然后调用do_IRQ函数,在do_IRQ函数中会调用(并非直接调用那么简单)到handle_IRQ_event函数,在此函数中会执行实际的中断服务例程。当中断服务例程执行完毕后,会返回到上面的那段汇编程序中,转入ret_from_intr代码段从中断返回。
在上述文字描述的基础上,可以通过下图进一步加深中断的处理过程:
从上面的概述中,我们可以很快定位我们未来要分析的函数:do_IRQ()和hand_IRQ_event();在分析上述函数之前,我们很有必要先分析一下关于中断的三个重要的数据结构。
2.struct irq_desc
在一开始我们分析中断的时候,谈及到了中断向量。在内核中,每个中断向量都有相应的有一个irq_desc结构体(稍早内核版本中为irq_desc_t)来描述一个中断向量(也就是中断源),具体代码如下: