(第三章 11)中断
一、中断回顾
??? 1. 中断的基本概念
????? 中断:在计算机科学中,中断是指由于接收到来自外围硬件(相对于中央处理器和内存)的异步信号或来自软件的同步信号,而进行相应的硬件/软件处理。发出这样的信号称为进行中断请求(interrupt request,IRQ)。硬件中断导致处理器通过一个上下文切换(context switch)来保存执行状态(以程序计数器和程序状态字等寄存器信息为主);软件中断则通常作为CPU指令集中的一个指令,以可编程的方式直接指示这种上下文切换,并将处理导向一段中断处理代码。中断在计算机多任务处理,尤其是实时系统中尤为有用。这样的系统,包括运行于其上的操作系统,也被称为“中断驱动的”(interrupt-driven)。
????? 中断是用以提高计算机工作效率、增强计算机功能的一项重要技术。最初引入硬件中断,只是出于性能上的考量。如果计算机系统没有中断,则处理器与外部设备通信时,它必须在向该设备发出指令后进行忙等待(Busy waiting),反复轮询该设备是否完成了动作并返回结果。这就造成了大量处理器周期被浪费。引入中断以后,当处理器发出设备请求后就可以立即返回以处理其他任务,而当设备完成动作后,发送中断信号给处理器,后者就可以再回过头获取处理结果。这样,在设备进行处理的周期内,处理器可以执行其他一些有意义的工作,而只付出一些很小的、切换上下文所引发的时间代价。后来被用于CPU外部与内部紧急事件的处理、机器故障的处理、时间控制等多个方面,并产生通过软件方式进入中断处理(软中断)的概念。
????? 在硬件实现上,中断可以是一个包含控制线路的独立系统,也可以被整合进存储器子系统中。对于前者,在IBM个人机上,广泛使用可编程中断控制器(Programmable Interrupt Controller,PIC)来负责中断响应和处理。PIC被连接在若干中断请求设备和处理器的中断引脚之间,从而实现对处理器中断请求线路(多为一针或两针)的复用。作为另一种中断实现的形式,即存储器子系统实现方式,可以将中断端口映射到存储器的地址空间,这样对特定存储器地址的访问实际上是中断请求。
中断可分为如下几种:
??? 1). 硬件中断
??? 可屏蔽中断(maskable interrupt)。硬件中断的一类,可通过在中断屏蔽寄存器中设定位掩码来关闭。
??? 非可屏蔽中断(non-maskable interrupt,NMI)。硬件中断的一类,无法通过在中断屏蔽寄存器中设定位掩码来关闭。典型例子是时钟中断(一个硬件时钟以恒定频率—如50Hz—发出的中断)。
??? 处理器间中断(interprocessor interrupt)。一种特殊的硬件中断。由处理器发出,被其它处理器接收。仅见于多处理器系统,以便于处理器间通信或同步。
??? 伪中断(spurious interrupt)。一类不希望被产生的硬件中断。发生的原因有很多种,如中断线路上电气信号异常,或是中断请求设备本身有问题。
??? 2). 软件中断
??? 软件中断。是一条CPU指令,用以自陷一个中断。由于软中断指令通常要运行一个切换CPU至内核态(Kernel Mode/Ring 0)的子例程,它常被用作实现系统调用(System call)。
??? 处理器通常含有一个内部中断屏蔽位(Sam: 在处理器中),并允许通过软件来设定。一旦被设定,所有外部中断都将被系统忽略。这个屏蔽位的存取速度显然快于中断控制器上的中断屏蔽寄存器(Sam: 在中断控制器上),因此可提供更快速地中断屏蔽控制。
??? 中断尽管可以提高计算机处理性能,但过于密集的中断请求/响应反而会影响系统性能。这类情形被称作中断风暴(interrupt storm)。
?
??? 2. 实例:8086A的中断系统
??? 8086A处理器是INTEL公司于20世纪70年代末推出的一款16位处理器。该处理器在中断处理上有如下特色,这些特色也为后续INTEL处理器所共有。
?
引脚NMI和INTR
??? 8086A提供两个中断引脚:第17引脚NMI,和第18引脚INTR。前者用于接收非可屏蔽中断,后者则接收可屏蔽中断。通常,INTR引脚与中断控制器(如8259A)相连,后者再分别与各设备的中断请求引脚连接。除了INTR外,8086A还将自身的16位地址总线(配合M/IO引脚并通过译码器)及8位数据总线与8259A连接,并将INTA引脚与8259A的同名引脚相连。
中断向量表和中断描述符表(IDT)
??? 在存储器地址空间中,规定最低的1K空间,即00000H到003FFH为中断向量表。全表共含256个中断向量,每个向量的长度为4字节,包含中断处理程序的起始地址。共有从0到255共256个中断类型码,每个中断类型码对应的中断向量所在地址为该类型码乘以4。举例而言,如果中断类型码为1,则对应中断向量所在地址为00004H;如果中断类型码为33,则对应中断向量所在地址为00084H。这样,如果已知一个中断类型码,则需要通过两次地址转换(中断类型码到中断向量表地址;中断向量表地址到中断处理程序地址)才能到达中断处理程序。另外应注意每一个中断向量所包含的地址是以低二字节存储偏移量,高二字节存储段地址的形式存储目标地址值的。
??? 在全部256个中断中,前32个(0—31)为硬件系统所预留。后224个可由用户设定。在初始化8259A时,可设定其上各中断引脚(共8条)对应的中断类型码。同时,将对应此中断之处理程序的起始地址保存在该中断类型码乘4的地址位中,作为中断向量。
??? 在INTEL后续的32位CPU中,使用中断描述符表来代替中断向量表。中断描述符表的起始地址由中断描述符表寄存器(IDTR)来定位,因此不再限于底部1K位置。另一方面,中断描述符表的每一个项目——称作门描述符——除了含有中断处理程序地址信息外,还包括许多属性/类型位。门描述符分为三类:任务门、中断门和自陷门。CPU对不同的门有不同的调用(处理)方式。
中断处理过程
??? 在实际运行中,一旦设备通过某引脚N向8259A发出中断指令,后者便向8086A的INTR引脚发送中断信号。8086A通过INTA引脚通知8259A中断有效(这个过程实际上还包括对此8259A的选址),后者即通过地址总线将对应引脚N的中断类型码(已预先存好,见上节)发送给CPU。CPU得到中断类型码后,先进行现场保护,主要包括:
??? 状态寄存器FLAGS压栈(同时堆栈寄存器SP-2);
??? 关闭中断(将FLAGS寄存器的IF位置零);
??? 将当前代码段寄存器CS和程序计数器IP压栈(同时堆栈寄存器SP-4)。
??? 现场保护完成后,CPU开始按照前述的两步骤翻译中断程序入口地址。在得到中断处理程序地址之后但调用中断处理程序之前,CPU会再检查一下NMI引脚是否有信号,以防在刚才的处理过程中忽略了可能的NMI中断。NMI的优先级始终高于INTR。
??? 注:INTR接收可屏蔽中断,NMI接收非可屏蔽中断。他们都是硬件中断。
??? 中断处理程序虽然是由程序员编写,但须循一定规范。作为例程,中断处理程序应该先将各寄存器信息(除了IP和CS,此二寄存器现已指向当前中断程序)压入堆栈予以保存,这样才能在中断处理程序内部使用这些寄存器。在程序结束时,应该按与压栈保护时相反的顺序弹出各寄存器的值。中断程序的最后一句始终是IRET指令,这条指令将栈顶6个字节分别弹出并存入IP、CS和FLAGS寄存器,完成了现场的还原。
??? 当然,如果是操作系统的中断处理程序,则未必——通常不会——还原中断前的状态。这样的中断处理程序通常会在调用完寄存器保存例程后,调用进程调度程序(多由高级语言编写),并决定下一个运行的进程。随后将此进程的寄存器信息(上次中断时保存下来的)存入寄存器并返回。在中断程序结束之后,主程序也发生了改变。
?
一些与中断控制相关的指令包括:
??? CLI 关闭中断(实为将FLAGS的IF位置0)。
??? STI 开启中断(实为将FLAGS的IF位置1)。
??? INT n 调用中断子程序,n为中断类型码。DOS系统有一个系统调用API即为INT 21H。
??? INT0 先判别FLAGS的OF位是否为1,如是则直接调用类型为4的中断子程序,用以处理溢出中断。
??? IRET 将栈顶6个字节分别弹出并存入IP、CS和FLAGS寄存器,用以返回中断现场。
?
二、
?
?