中断控制是计算机发展中一个重要的技术。最初它是为了克服对I/O接口控制采用程序查询所带来的处理器效率低而产生的。中断控制的主要优点是只有在I/O需要服务时才能得到处理器的响应,而不需要处理器不断的进行查询。由此,最初的中断全部都是对外部设备而言的,称为外部中断(或硬件中断)。
但随着计算机系统结构的不断改进以及应用技术的日益提高,中断的适用范围也随之扩大,出现了所谓的内部中断(或叫异常),它是为了解决机器运行时所出现的某些随机事件及编程方便而产生的。形成了一个完整的中断系统。
1.中断向量
Intel x86系列微机共支持256种向量中断,为使处理器较容易的识别每种中断源,将他们从0~255编号,即赋予一个中断类型码n,Intel把这个8位的无符号整数叫做一个向量,一次,也叫做中断向量。所有256种中断可分为两大类:中断和异常。异常又被分为故障和陷阱,他们的共同特点是即不使用中断控制器,也不能被屏蔽(异常其实是CPU发出的中断信号)。中断又分为外部可屏蔽中断(INTR)和外部非屏蔽中断(NMI),所有I/O设备产生的中断请求(IRQ)均引起屏蔽中段,而紧急的事件(如硬件故障)引起的故障产生非屏蔽中断。
非屏蔽中断的向量和异常的向量是固定的,而屏蔽中断的向量是可以通过对中断控制器的变成来改变的。Linux对256个向量的分配如下。
(1)从0~31的向量对应异常和非屏蔽中断。
(2)从32~47的向量(即由I/O设备引起的中断)非配给屏蔽中断。
(3)剩余的从48~255的向量用来表示软中断。Linux只用了其中的一个(即128或0x80向量)用来实现系统调用。
可以通过 cat /proc/interrupts来查看当前系统中各种外设的IRQ命令。
2.外设可屏蔽中断
如图,我们把与中断控制器相连的每条线叫做中断线,要使用中断线,就要进行中断线的申请,也就是IRQ。IRQ线是从0开始顺序编号的,因此,第一条IRQ线通常表示成IRQ0。IRQn的缺省向量是n+32,如前所述,IRQ和向量之间的映射可以通过中断控制器端口来修改。
并不是每个设备都可以向中断线上发送中断信号的,只有对某一条确定的中断线拥有了控制权,才可以向这条中断线上发送信号。由于计算机的外部设备越来越多,所以15条中断线已经不够用了,中断线是十分宝贵的资源,所以只有当设备需要中断的时候才申请占用一个IRQ,或者是在申请IRQ时采用共享中断的方法,这样可以让更多的设备使用中断。
对于外部I/O请求的屏蔽可以分为两种情况,一种是从CPU的角度也就是清除EFLAG的中断标志位(IF),当IF=0时,禁止任何外部I/O中断请求,即关中断;一种就是从中断控制器的角度,因为中断控制器中有一个8位的中断屏蔽寄存器,每位对应8259A中的一条中断线,如果要禁用某条中断线,则把中断屏蔽寄存器相应的位置1,要启用,则置为0。
3.异常即非屏蔽中断
异常就是CPU内部出现的中断,也就是说,在CPU执行特定指令时出现的非法情况。非屏蔽中断就是计算机内部硬件出错时引起的异常情况。两者与外部I/O接口没有人关系。Intel把非屏蔽中断作为异常的一种来处理,因此,后面所提到的异常也包括了非屏蔽中断。在CPU执行一个异常处理程序时,就不再为其他异常或可屏蔽中断请求服务,也就是说,当某个异常被响应后,则由CPU锁住(CPU具有缓冲异常的能力),待这个异常处理完后,才响应被锁存的异常。这里讨论的异常中断向量在0~31之间,不包括系统调用(中断向量为0x80)。
Linux内核必须为每种异常提供一个专门的异常处理程序。
4.中断描述符表
在实地址模式中,CPU把内存中从0开始的1KB用来存储中断向量表。中断向量表也叫做中断描述表IDT。其中的每一个表项叫做一个门描述符,“门”的含义是当中断发生时必须先通过这些门,然后才能进入相应的处理程序。门描述一般格式如下图所示:
其中类型占3位,表示门描述符的类型,主要门描述符有以下几种。
(1)中断门
其类型码为110,中断门包含一个中断或异常处理程序所在段的选择符和段内偏移量。当控制权通过中断门进入中断处理程序时,处理器清IF标志,即关中断,以避免嵌套中断(中断嵌套就是当处理中断时,有另一个优先级更高的中断请求,则会暂时终止当前的中断服务,去处理优先级高的中断,待处理完毕,再回到被终止的中断服务程序继续执行)的发生。中断门中的请求特权级(DPL)为0,因此,用户态的进程不能访问Intel的中断门。所有的中断处理程序都由中断门激活,并全部限制在内核态。
(2)陷阱门
其类型码为111,与中断门类似,其唯一的区别就是码控制权通过陷阱门进入处理程序时维持IF标志位不变,也就是说不关中断。
(3)系统门
这是Linux内核特别设置的,用来让用户态的进程访问Intel的陷阱门,因此,门描述符的DPL为3。系统调用就是通过系统门进入内核的。
最后,在保护模式下,中断描述符表在内存的位置不再局限于从地址0开始的地方,而是可以放在内存的任何地方。为此,CPU中增设了一个中断描述符表寄存器IDTR,用来存放中断描述符表的起始地址。中断描述符表寄存器IDTR是一个48为的寄存器,其低16位保存中断描述表的大小,高32位保存中断描述符表的界限。