努力 奋斗 专注于linux内核 微信:tolimit 扣扣:348958453
2015年(26)
分类: LINUX
2015-04-28 14:41:22
为了方便说明,这里我们将PIC和APIC统称为中断控制器。中断控制器是作为中断(IRQ)和CPU核之间的一个桥梁而存在的,每个CPU内部都有一个自己的中断控制器,中断线并不是直接与CPU核相连,而是与CPU内部或外部的中断控制器相连。而为什么叫做可编程中断控制器,是因为其本身有一定的寄存器,CPU可以通过操作设置中断控制器屏蔽某个中断引脚的信号,实现硬件上的中断屏蔽。中断控制器也可以级联提供更多的中断线,具体如下:
如上图,CPU的INTR与中断控制器的INT相连,INTA与ACK相连,当一个外部中断发生时(比如键盘中断IRQ1),中断控制器与CPU交互操作如下:
在linux内核中,用struct irq_chip结构体描述一个可编程中断控制器,它的整个结构和调度器中的调度类类似,里面定义了中断控制器的一些操作,如下:
在中断系统中有两个名字很相像的结构,就是中断描述符表和中断描述符数组。这里我们先说说中断描述符表。 一个系统中的中断和异常加起来一共是256个,它们以向量的形式保存在中断描述符表中,每一个向量是8字节(整个表大小就是8 x 256=2048字节),其主要保存着权限位和向量对应的中断或异常处理程序的入口地址。而一般的,linux会将中断描述符表中的0~31用于非屏蔽中断和异常,其他的中断用于32~255之间。CPU把中断描述符表的向量类型分为三种类型:任务门、中断门、陷阱门。CPU为了防止恶意程序访问中断,限制了中断门的权限,而在某些时候,用户程序又必须使用中断,所以Linux把中断描述符的中断向量类型改为了5种:中断门,系统门,系统中断门,陷阱门,任务门。
当我们发生异常或中断时,系统首先会判断权限字段(安全处理),权限通过则进入指定的处理函数,而所有的中断门的中断处理函数都是同一个,它首先是一段汇编代码,汇编代码操作如下:
每个能够产生中断的设备或者模块都会在内核中注册一个中断服务例程(ISR),当产生中断时,中断处理程序会被执行,在中断处理程序中,首先会保存中断向量号和上下文,之后执行中断线对应的中断服务例程。对于CPU来说,中断线是非常宝贵的资源,而由于计算机的发展,外部设备数量和种类越来越多,导致了中断线资源不足的情况,linux为了应对这种情况,实现了两种中断线分配方式,分别是:共享中断线,中断线动态分配。
多个设备共用一条中断线,当此条中断线发生中断时,因为不可能预先知道哪个特定的设备产生了中断,因此,这条中断线上的每个中断服务例程都会被执行,以验证是哪个设备产生的中断(一般的,设备产生中断时,会标记自己的状态寄存器,中断服务例程通过检查每个设备的状态寄存器来查找产生中断的设备)。
一条中断线在可能使用的时刻才与一个设备驱动程序关联起来,这样一来,即使几个硬件设备并不共享中断线,同一个中断向量也可以由这几个设备在不同时刻运行。
共享中断线的分配方式是比较常见的,一次典型的基于共享中断线的中断处理流程如下:
由于中断处于中断上下文中,所以在中断处理过程中,会有以下几个特性:
中断描述符用于描述IRQ线的属性与状态,每个IRQ都有它自己的中断描述符,这些中断描述符用一个数组保存, 这个数组就是中断描述符数组,整个中断描述符数组长度为NR_IRQS(通常为224)项。当产生一个中断或者异常时,首先会从中断描述符表中获取到一个中断向量号时(此中断向量号有可能表示中断,也可能表示的是一个异常),如果是一个中断导致的,会执行do_IRQ()函数,而在do_IRQ()函数中,会根据中断向量号,从中断描述符数组中获取对应的中断描述符,如下图:
中断服务例程用于描述一个设备的中断处理(区别与中断处理函数),每个申请了中断的外部设备都会有一个中断服务例程,其作用就是执行对应设备的中断处理。当多个设备共享IRQ线时,内核会将此IRQ线上所有设备的中断服务例程组织成一个链表并保存在中断描述符中,当此IRQ线产生中断时,中断处理函数会依次执行此IRQ线上的中断服务例程。内核使用struct irqaction描述一个中断服务例程:
此数组包含NR_CPUS个元素,系统中每个CPU对应数组中的一个元素。每个元素的类型为irq_cpustat_t,其包含几个计数器和内核记录CPU正在做什么的标志。
到此,在中断处理中所涉及的几个重要的数据结构已经说明,其最主要的数据结构为:中断描述符(struct irq_desc),中断控制器描述符(struct irq_chip),中断服务例程(struct irqaction)。它们的组织形式如下: