<一>80386的中断和异常
8086/8088把中断分为内部中断和外部中断两大类。为了支持多任务和虚拟存储器等功能,80386把外部中断称为“中断”,把内部中断称为“异常”。与8086/8088一样,80386通常在两条指令之间响应中断或异常。80386最多处理256种中断或异常。
1.中断
对80386而言,中断是由异步的外部事件引起的。外部事件及中断响应与正执行的指令没有关系。通常,中断用于指示I/O设备的一次操作已完成。与8086/8088一样,80386有两根引脚INTR和NMI接受外部中断请求信号。INTR接受可屏蔽中断请求。NMI接受不可屏蔽中断请求。在80386中,标志寄存器EFLAGS中的IF标志决定是否屏蔽可屏蔽中断请求。
外部硬件在通过INTR发出中断请求信号的同时,还要向处理器给出一个8位的中断向量。处理器在响应可屏蔽中断请求时,读取这个由外部硬件给出的中断向量号。处理器对这个中断向量号并没有规定。但在具体的微机系统中,系统必须通过软件和硬件的配合设置,使得给出的这个中断向量号不仅与外部中断源对应,而且要避免中断向量号使用冲突情况的出现。可编程中断控制器芯片8259A可配合80386工作,能够根据设置向处理器提供上述中断向量号,还能处理中断请求的优先级。每个8259A芯片可以支持8路中断请求信号,如果使用9个8259A芯片(一个主片,8个从片),就可使80386在单个引脚INTR上接受多达64个中断源的中断请求信号。
处理器不屏蔽来自NMI的中断请求。处理器在响应NMI中断时,不从外部硬件接收中断向量号。与8086/8088一样,在80386中,不可屏蔽中断所对应的中断向量号固定为2。为了不可屏蔽中断的嵌套,每当接受一个NMI中断,处理器就在内部屏蔽了再次响应NMI,这一屏蔽过程直到执行中断返回指令IRET后才结束。所以,NMI处理程序应以IRET指令结束。
2.异常
异常是80386在执行指令期间检测到不正常的或非法的条件所引起的。异常与正执行的指令有直接的联系。例如,执行除法指令时,除数等于0。再如,执行指令时发现特权级不正确。当发生这些情况时,指令就不能成功完成。软中断指令“INT n”和“INTO”也归类于异常而不称为中断,这是因为执行这些指令产生异常事件。
80386识别多种不同类别的异常,并赋予每一种类别以不同的中断向量号。异常发生后,处理器就象响应中断那样处理异常。即根据中断向量号,转相应的中断处理程序。把这种中断处理程序称为异常处理程序可能更合适。
根据引起异常的程序是否可被恢复和恢复点不同,把异常进一步分类为故障(Fault)、陷阱(Trap)和中止(Abort)。我们把对应的异常处理程序分别称为故障处理程序、陷阱处理程序和中止处理程序。
故障是在引起异常的指令之前,把异常情况通知给系统的一种异常。80386认为故障是可排除的。当控制转移到故障处理程序时,所保存的断点CS及EIP的值指向引起故障的指令。这样,在故障处理程序把故障排除后,执行IRET返回到引起故障的程序继续执行时,刚才引起故障的指令可重新得到执行。这种重新执行,不需要操作系统软件的额外参与。故障的发现可能在指令开始执行之前,也可能在指令执行期间。如果在指令执行期间检测到故障,那么中止故障指令,并把指令的操作数恢复为指令开始执行之前的值。这可保证故障指令的重新执行得到正确的结果。例如,在一条指令的执行期间,如果发现段不存在,那么停止该指令的执行,并通知系统产生段故障,对应的段故障处理程序可通过加载该段的方法来排除故障,之后,原指令就可成功执行,至少不再发生段不存在的故障。
陷阱是在引起异常的指令之后,把异常情况通知给系统的一种异常。当控制转移到异常处理程序时,所保存的断点CS及EIP的值指向引起陷阱的指令的下一条要执行的指令。下一条要执行的指令,不一定就是下一条指令。因此,陷阱处理程序并不是总能根据保存的断点,反推确定出产生异常的指令。在转入陷阱处理程序时,引起陷阱的指令应正常完成,它有可能改变了寄存器或存储单元。软中断指令、单步异常是陷阱的例子。
中止是在系统出现严重情况时,通知系统的一种异常。引起中止的指令是无法确定的。产生中止时,正执行的程序不能被恢复执行。系统接收中止后,处理程序要重新建立各种系统表格,并可能重新启动操作系统。硬件故障和系统表中出现非法值或不一致的值是中止的例子。
3.优先级
在一条指令执行期间,入检测到不只一个中断或异常,那么按下表所列优先级通知系统。把优先级最高的中断或异常通知系统,其它优先级较低的异常被废弃,而优先级较高的中断则保持悬挂。
80386响应 中断/异常 的优先级 |
中断/异常类型 | 优先级 |
调试故障 | 最高 | |
其它故障 | ↓ | |
陷阱指令INT n和INTO | ↓ | |
调试陷阱 | ↓ | |
NMI中断 | ↓ | |
INTR中断 | 最低 |
<二>异常类型
象中断分为多种类型一样,异常也可分为多种类型。
1.80386识别的异常
80386识别的多种不同类别的异常及赋予的对应中断向量号如下表所示。某些异常还以出错码的形式提供一些附加信息传递给异常处理程序,出错代码列中的“无”表示没有出错代码,“有”表示有出错代码。
异
常 一 览 表 |
向量号 | 异常名称 | 异常类型 | 出错代码 | 相关指令 |
0 | 除法出错 | 故障 | 无 | DIV,IDIV | |
1 | 调试异常 | 故障/陷阱 | 无 | 任何指令 | |
3 | 单字节INT3 | 陷阱 | 无 | INT 3 | |
4 | 溢出 | 陷阱 | 无 | INTO | |
5 | 边界检查 | 故障 | 无 | BOUNT | |
6 | 非法操作码 | 故障 | 无 | 非法指令编码或操作数 | |
7 | 设备不可用 | 故障 | 无 | 浮点指令或WAIT | |
8 | 双重故障 | 中止 | 有 | 任何指令 | |
9 | 协处理器段越界 | 中止 | 无 | 访问存储器的浮点指令 | |
0AH | 无效TSS异常 | 故障 | 有 | JMP、CALL、IRET或中断 | |
0BH | 段不存在 | 故障 | 有 | 装载段寄存器的指令 | |
0CH | 堆栈段异常 | 故障 | 有 | 装载SS寄存器的任何指令、对SS寻址的段访问的任何指令 | |
0DH | 通用保护异常 | 故障 | 有 | 任何特权指令、任何访问存储器的指令 | |
0EH | 页异常 | 故障 | 有 | 任何访问存储器的指令 | |
10H | 协处理器出错 | 故障 | 无 | 浮点指令或WAIT | |
11H—0FFH | 软中断 | 陷阱 | 无 | INT n |
由上表可见,保护模式下的某些中断向量号的分配与实模式的中断向量号发生了冲突。实模式下的中断向量号的分配基于PC微机系统的8086/8088 CPU,上表中的中断向量号的分配是80386所规定的。实际上,Intel在宣布8086/8088时,保留了这些发生冲突的中断向量号。尽管发生这样的冲突,但以80386为CPU的微机系统仍可保持与以8086/8088为CPU的微机系统的兼容,原因是在80386的实模式下,几乎不发生那些中断向量号与外部硬件中断请求时所提供的中断向量号存在冲突的异常。需要注意的是,在保护模式下必须重新设置8259A中断控制器,以产生不与异常相冲突的硬件中断向量。
2.故障类异常
当发生故障,控制转移到故障处理程序时,所保存的断点CS及EIP的值指向引起故障的指令,以便在排除故障后恢复执行。
(1)除法出错故障(异常0)
除法出错是一种故障。当执行DIV指令或IDIV指令时,如果除数等于0或者商太大,以至于存放商的操作数容纳不下,那么就产生这一故障。除法出错故障不提供出错码。
(2)边界检查故障(异常5)
如果BOUND指令发现被测试的值超过了指令中给定的范围,那么就发生边界检查故障。边界检查故障不提供出错码。
(3)非法操作码故障(异常6)
如果80386不能把CS和EIP所指向存储单元的位模式识别为某条指令的部分,那么就发生非法操作码故障。当出现如下情况时,发生这样的故障:(1)操作码字段的内容不是一个合法的80386指令代码;(2)要求使用存储器操作数的场合,使用了寄存器操作数;(3)不能被加锁的指令前使用了LOCK前缀。非法操作码故障不提供操作码。
(4)设备不可用故障(异常7)
设备不可用故障支持80387数字协处理器。在没有80387协处理器硬件的系统中,可用该异常的处理程序代替协处理器的软件模拟器。在发生任务切换时,使得只有在新任务使用浮点指令时,才进行80387寄存器状态的切换。设备不可用故障不提供出错码。该故障在下列情况下产生:(1)在执行浮点指令时,控制寄存器CR0中的EM位或TS位为1;(2)在执行WAIT指令时,控制寄存器CR0中TS位及EM位都为1。需要注意的是,本异常的处理程序必须是一个过程而不能是任务,否则当处理程序发布一条IRET指令时,80386就设置TS位。然后协处理器再次执行这个发生故障的指令,发现TS是置位的,因此就再次发生异常7,结果是无休止的循环。处理程序能通过陷阱门被调用,因为执行期间可以允许中断。
(5)无效TSS故障(异常0AH)
当正从任务状态段TSS装入选择子时,如果发生了除了段不存在故障以外的段异常时,就发生无效TSS故障。在进入故障处理程序时,保存的CS及EIP指向发生故障的指令;或者该故障作为任务切换的一部分发生时,指向任务的第一条指令。
无效TSS故障提供了一个出错码,出错码的格式如下图所示。其中选择子部分是指向引起故障的TSS的选择子。16位的出错代码的主要成分是选择子,指向引起故障的TSS的选择子。高13位是选择子的索引部分,TI位是描述符表指示位。
出错代码的格式 | |||
BIT15—BIT3 | BIT2 | BIT1 | BIT0 |
选择子的索引部分 | TI | IDT | EXT |
上图所示出错码格式是异常时出错码的一般格式。从图中可见出错码中不含选择子的RPL,而由IDT位和EXT位代替。当处理某一异常或外部中断时,又发生了某种异常,那么EXT位置1。当从中断描述符表IDT中读出表项并产生异常时,IDT位置1,这只在中断或异常的处理期间才会发生。当没有选择子时,构成出错码选择子部分的值为0。
一些引起无效TSS故障的原因如下:
TSS描述符中的段限长小于103;
无效的LDT描述符,或者LDT未出现;
堆栈段不是一个可写段;
堆栈段选择子索引的描述符超出描述符表界限;
堆栈段DPL与新的CPL不匹配;
堆栈段选择子的RPL不等于CPL;
代码段选择子索引的描述符超出描述符表界限;
代码段选择子不指向代码段;
非一致代码段的DPL不等于新的CPL;
一致代码段的DPL大于新的CPL;
对应DS、ES、FS或GS的选择子指向一个不可读段(如系统段);
对应DS、ES、FS或GS的选择子索引的描述符超出描述符表的界限。
(6)段不存在故障(异常0BH)
处理器在把描述符装入非SS段寄存器的高速缓冲时,如果发现描述符其它方面有效,而P位为0(表示对应段不存在),那么在引用此描述符时就发生段不存在故障。有关SS段的情形纳入堆栈段故障。在进入故障处理程序时,保存的CS及EIP执行发生故障的指令;或者该故障作为任务切换的一部分发生时,指向任务的第一条指令。
段不存在故障提供了一个包含引起该故障的段选择子的出错代码。出错码的格式如上图所示。选择子索引部分为引起段不存在故障的段描述符选择子的索引。
(7)堆栈段故障(异常0CH)
当处理器检测到用SS寄存器进行寻址的段有关的某种问题时,就发生堆栈段故障。在进入故障处理程序时,保存的CS及EIP指向发生故障的指令;或者该故障作为任务切换的一部分发生时,指向任务的第一条指令。堆栈段故障提供一个出错码,出错码的格式也如上图。
具体地说,当出现下列三种情况时,将引起堆栈段故障:
(1)在堆栈操作时,偏移超出段界限所规定的范围。这种情况下的出错码是0。例如PUSH操作时,堆栈溢出。
(2)在由特权级变换所引起的对内层堆栈的操作时,偏移超出段界限所规定的范围。这种情况下的出错码包含有内层堆栈的选择子。
(3)装入到SS寄存器(高速缓冲寄存器)的描述符中的存在位为0。这种情况下的出错码包含有对应的选择子。
上述第一种情况是容易辨别的。第二和第三种情况的辨别要通过判断出错代码所含选择子所指示的描述符中的存在位进行。如果存在位为1,那么是第二种情况;否则是第三种情况。
(8)通用保护故障(异常0DH)
除了明确列出的段异常外,其它的段异常都被视为通用保护故障。在进入故障处理程序时,保存的CS及EIP指向发生故障的指令;或者该故障作为任务切换的一部分发生时,指向任务的第一条指令。通用保护故障提供一个出错码,出错码的格式也如上图所示。
根据处理程序可能作出的响应,通用保护故障可分为如下两类:
(1)违反保护方式,但程序无须中止的异常。这类故障提供的出错码为0。这种异常在应用程序执行特权指令或I/O访问时发生,支持虚拟8086程序的系统或支持虚拟I/O访问的系统需要模拟这些指令,并在模拟完成产生故障的指令后,重新执行被中断的程序。
(2)违反保护方式,并导致程序终止的异常。这类故障提供的出错码可能为0,也可能不为0(能确定选择子时)。引起这类故障的一些原因如下:
向某个只读数据段或代码段写;
从某个只能执行的代码段读出;
将某个系统段描述符装入到数据段寄存器DS、ES、FS、GS或SS;
将控制转移到一个不可执行的段;
在通过段寄存器CS、DS、ES、FS或GS访问内存时,偏移超出段界限;
当访问某个描述符表时,超过描述符表段界限;
把PG位为1但PE位为0的控制信息装入到CR0寄存器;
切换到一个正忙的任务。
对上述两类通用保护故障的辨别,可通过检查引起故障的指令和出错码进行。如果出错码非0,那么肯定是第二类通用保护故障。如果出错码是0,那么需要进一步检查引起故障的指令,以确定它是否是系统支持的可以模拟的指令。
(9)页故障(异常0EH)
关于页故障的详细说明请见后面的文章。
(10)协处理器出错(异常10H)
协处理器出错故障指示协处理器发生了未被屏蔽的数字错误,如上溢或下溢。在引起故障的浮点指令之后的下一条浮点指令或WAIT指令,把协处理器出错作为一个故障通知给系统。协处理器出错故障不提供出错码。
3.陷阱类异常
(1)调试陷阱(异常1)
调试异常有故障类型,也有陷阱类型。调试程序可以访问调试寄存器DR6,以确定调试异常的原因和类型。调试异常不提供出错码。
(2)单字节INT3(异常3)
INT3是一条特别的单字节“INT n”指令。调试程序可利用该指令支持程序断点。INT3指令被看成是一种陷阱,而不是一个中断。当由于执行INT3指令进入异常3处理程序时,被保存的CS和EIP指向紧跟INT3的指令,即INT3后面的字节。INT3陷阱不提供出错码。
(3)溢出(异常4)
INTO指令提供条件陷阱。如果OF标志为1,那么INTO指令产生陷阱;否则不产生陷阱,继续执行INTO后面的指令。在进入溢出处理程序时,被保存的CS和EIP指向INTO指令的下一条指令。溢出陷阱不提供出错码。
4.中止类异常
(1)双重故障异常(异常8)
当系统正在处理一个异常时,如果又检测到一个异常,处理器试图向系统通知一个双重故障,而不是通知第二个异常。双重故障属于中止类异常,所以在转入双重故障处理程序时,被保存的CS和EIP可能不指向引起双重故障的指令,而且指令的重新启动不支持双重故障。双重故障提供的出错码为0。
当正处理一个段故障异常时,有可能又产生一个页故障。在这种情况下,通知给系统的是一个页故障异常而不是双重故障异常。但是,如果正处理一个段故障或页故障时,又一个段故障被检测到;或者如果正处理一个页故障时,又一个页故障被检测到,那么就引起双重故障。
当正处理一个双重故障时,又一个段或页故障被检测到,那么处理器暂停执行指令,并进入关机方式。关机方式类似于处理器指令一条HLT指令后的状态:处理器空转,并维持到处理器接收到一个NMI中断请求或者被重新启动为止。在关机方式下,处理器不响应INTR中断请求。
双重故障通常指示系统表出现严重的问题,例如段描述符表、页表或中断描述符表出现问题。双重故障处理程序在重建系统表后,可能不得不重新启动操作系统。
(2)协处理器段越界(异常9)
协处理器段越界异常属于中止类异常,这是因为引起该异常的指令不能被重新启动。当浮点指令操作数超出段界限时,产生该中止异常。协处理器段越界异常不提供出错码。在异常处理程序入口保存的CS及EIP指向被中止的指令。这种中止不是系统的中止,而是只影响检测到这种异常时正执行的指令所在的程序。
<三>中断和异常的转移方法
80386实模式下的中断和异常的转移方法与8086相同。这里介绍的中断和异常的转移方法是指 80386在保护模式下响应中断和处理异常时所采用的转移方法。
1.中断描述符表IDT
与8086/8088一样,在响应中断或者处理异常时,80386根据中断向量号转对应的处理程序。但是,在保护模式下,80386不使用实模式下的中断向量表,而是使用中断描述符表IDT。在保护模式下,80386把中断向量号作为中断描述符表IDT中描述符的索引,而不再是中断向量表中的中断向量的索引。
象全局描述符表GDT一样,在整个系统中,中断描述符表IDT只有一个。中断描述符表寄存器IDTR指示IDT在内存中的位置。由于80386只识别256个中断向量号,所以IDT最大长度是2K。
中断描述符表IDT所含的描述符只能是中断门、陷阱门和任务门。也就是说,在保护模式下,80386只有通过中断门、陷阱门或任务门才能转移到对应的中断或异常处理程序。
从前文中给出的门描述符的格式可见,门描述符包含由选择子和偏移构成的48位全指针。另外,双字计数字段对中断门、陷阱门和任务门而言无意义。
2.中断响应和异常处理的步骤
由硬件自动实现的中断响应和异常处理的步骤如下:
首先,判断中断向量号要索引的门描述符是否超出IDT的界限。若超出界限,就引起通用保护故障,出错码为中断向量号乘8再加2。
其次,从IDT中取得对应的门描述符,分解出选择子、偏移量和描述符属性类型,并进行有关检查。描述符只能是任务门、286中断门、286陷阱门、386中断门或386陷阱门,否则就引起通用保护故障,出错码是中断向量号乘8再加2。如果是由INT n指令或INTO指令引起转移,还要检查中断门、陷阱门或任务门描述符中的DPL是否满足CPL<=DPL(对于其它的异常或中断,门中的DPL被忽略)。这种检查可以避免应用程序执行INT n指令时,使用分配给各种设备用的中断向量号。如果检查不通过,就引起通用保护故障,出错码是中断向量号乘8再加2。门描述符中的P位必须是1,表示门描述符是一个有效项,否则就引起段不存在故障,出错码是中断向量号乘8再加2。
最后,根据门描述符类型,分情况转入中断或异常处理程序。
对于异常处理,在开始上述步骤之前,还要根据异常类型确定返回点;如果有出错代码,则形成符合出错码格式的出错码,并在实际执行异常处理程序之前把出错码压入堆栈。为了保证栈的双字边界对齐,16位的出错码以32位的值压入,其中高16位的值未作定义,对于16位段也是如此。
3.通过中断门或陷阱门的转移
如果中断向量号所指示的门描述符是386中断门或386陷阱门,那么控制转移到当前任务的一个处理程序过程,并且可以变换特权级。与其它调用门的CALL指令一样,从中断门和陷阱门中获取指向处理程序的48位全指针。其中16位选择子是对应处理程序或代码段的选择子,它指示全局描述符表GDT或局部描述符表LDT中的代码段描述符;32位偏移指示处理程序入口点在代码段内的偏移量。
通过中断门或陷阱门的转移过程如下所示,该过程由硬件自动进行。
(1)若选择子为空,则产生通用保护故障;
(2)取对应的描述符;
(3)若非存储段描述符,则产生通用保护故障;
(4)若非一致代码段且DPL
(5)调整RPL=0;
(6)把描述符装入CS;
(7)若入口偏移越界,则产生通用保护故障;
(8)EFLAGS压入堆栈;
(9)CS压入堆栈;
(10)EIP压入堆栈;
(11)使TF=0,NT=0;
(12)若为中断门,则使IF=0;
(13)若有出错码,则把出错码压入堆栈;
(14)转入处理程序。
由上述转移过程可见,中断门或陷阱门中指示处理程序的选择子必须指向描述一个可执行的代码段的描述符。如果选择子为空,就引起通用保护故障,出错码是0。如果描述符不是代码段描述符,就引起通用保护故障,出错码含选择子。
中断或异常可以转移到同一特权级或内层特权级。上述指定处理程序代码段的描述符中的类型及DPL字段,决定了这种同一任务内的转移是否要发生特权级变换。如果是一个非一致代码段,并且DPLCPL则产生通用保护异常。
上述转移过程中的第六步,也就是“把描述符装入CS”,是指把上述指定处理程序段的描述符装入CS的高速缓冲寄存器中,在这一步骤中要对描述符进行类似通过调用门进行转移的其它检查,包括是否代码段描述符和代码段描述符是否存在等,因此可能再发生异常。在对该描述符进行检查时,通过调整门中选择子的RPL=0(在处理器内部调整,而不影响存储器中的选择子的RPL字段)的方法,实现只考虑代码段的DPL,而不考虑门中选择子的RPL。把描述符装入CS之后,还要检查门描述符中给出的表示处理程序代码段入口的偏移是否越界,即是否超出段界限。如果越界,就引起出错码为0的通用保护故障。
从转移过程还可以看出,把标志寄存器和断点压入堆栈的做法和顺序与实模式是相同的,但这里每一次堆栈操作是一个双字,CS被扩展成32位。在16位段中亦是如此。
把TF置成0,表示不允许处理程序单步执行。把NT置成0,表示处理程序在利用中断返回指令IRET返回时,返回到同一任务而不是一个嵌套任务。需要注意的是,任何特权级的程序都可改变NT位,这样可以利用中断或陷阱处理程序完成任务切换。
通过中断门的转移和通过陷阱门的转移之间的差别只是对IF标志的处理。对于中断门,在转移过程中把IF置为0,使得在处理程序执行期间屏蔽掉INTR中断(当然,在中断处理程序中可以人为设置IF标志打开中断,以使得在处理程序执行期间允许响应可屏蔽中断);对于陷阱门,在转移过程中保持IF位不变,即如果IF位原来是1,那么通过陷阱门转移到处理程序之后仍允许INTR中断。因此,中断门最适宜于处理中断,而陷阱门适宜于处理异常。
在有出错码的情况下,转入处理程序之前还要把出错码压入堆栈。只有异常处理才可能有出错码。下图给出了通过中断门或陷阱门转移时的堆栈情况。(a)是没有变换特权级和没有出错码的情形;(b)是没有变换特权级有出错码的情形;(c)是变换特权级和没有出错码的内层堆栈的情形。(d)是变换特权级和有出错码的内层堆栈情形。注意图中每一项为双字。
4.通过任务门的转移
如果中断向量号所只是的门描述符是任务门描述符,那么控制转移到一个作为独立的任务方式出现的处理程序。任务门中的选择子是指向描述对应处理程序任务的TSS段的选择子,即该选择子指示一个可用的286TSS或386TSS。通过任务门的转移与通过任务门到一个可用的386TSS的段间调用指令CALL的转移很相似,主要的区别是,对于提供出错码的异常处理,在完成任务切换之后,把出错码压入新任务的堆栈中(通过任务门进行转移时,返回地址和外层栈指针不压入新任务的堆栈)。
通过任务门的转移,在进入中断或异常处理程序时,标志寄存器EFLAGS中的NT位被置为1,表示是嵌套任务,则IRET指令返回时,沿TSS中的链接字段返回到最后一个被挂起的任务。
在响应中断或处理异常时,使用任务门可提供一个处理程序任务的自动调度。这种任务调度由硬件直接执行,并且越过包含在操作系统中的软件任务切换,这就为处理程序提供了一个快速的任务切换。
5.转移方法的比较
对中断的响应和异常的处理,80386允许通过使用中断门或陷阱门实现由当前任务之内的一个过程进行处理;也允许通过使用任务门实现由另一个任务进行处理。在当前任务之内的处理程序较为简单,并可以很快地转移到处理程序,但处理程序要负责保存及恢复处理器的寄存器等内容。转到不同任务的处理程序要花费较长时间,保存及恢复处理器寄存器内容的开销作为任务切换的一部分。使用当前任务内的处理程序的方法,在响应中断或处理异常时,对正执行任务的状态可直接进行访问,但是这就要求每一个任务之内都包含一个处理程序。使用独立任务的处理方法,使处理程序得到较好的隔离,但在响应中断或处理异常时,对原任务状态的访问变得较为复杂。需要注意得是,有些异常必须由中断门或陷阱门进行处理,如上面提到得异常7。
无效TSS异常必须使用任务门进行处理,以保证处理程序有一个有效得任务环境。其它得异常通常在任务环境之内进行处理。在任务内,异常被检测并且不必屏蔽中断,所以,所以使用陷阱门。由陷阱门指示的异常处理程序是一个由所有任务共享的过程,所以该处理程序最好置于全局地址空间之内。如果各个任务要求有不同的处理程序,那么全局异常处理程序可保存一个各处理程序的入口表,并为引起异常的任务调用相应的处理程序。
中断通常与正执行的任务没有关系,并可能从使用任务门提供的隔离中获得好处。要求较快响应的中断,通过中断门可以得到较好的处理。因为中断随时都可能发生,所以,通过中断门访问的中断处理程序,必须置于全局地址空间中,以便对所有的任务都有效。还需强调的是,80386程序绝不能调用一个低特权级的过程,当处理器调用一个中断或异常处理程序时,它实施相同的规则,所以,这样一个过程必须至少具有与在其任务上下文中被调用的、由任务所执行的最高特权级的过程相同的特权级。因此,在使用中断门时,中断处理程序通常必须被安排在特权级0,否则若正在特权级0执行时发生中断,则不能进入中断处理程序,而会引起通用保护故障(中断处理程序使用任务门时除外,因为任务切换可以从任何特权级切换到目标任务的任何特权级)。
6.中断或异常处理后的返回
中断返回指令IRET用于从中断或异常处理程序的返回。该指令的执行根据任务嵌套标志NT位是否为1分为两种情形。
NT位为1,表示是嵌套任务的返回。当前TSS中的链接字段保存有前一任务的TSS的选择子,取出该选择子进行任务切换就完成了返回。这种情形在由通过任务门转入的中断或异常处理程序返回时出现,因为在由中断门或陷阱门转入处理程序时,NT位已被清0。
NT位为0,表示当前任务内的返回。这种情形在由通过中断门或陷阱门转入的中断或异常处理程序返回时出现。具体进行的操作包括:从堆栈顶弹出返回指针EIP及CS,然后弹出EFLAGS值。弹出的CS选择子的RPL字段,确定返回后的特权级。如果返回选择子的RPL与CPL相同,则不进行特权级改变。若RPL规定了一个外层特权级,则需要特权级改变,从内层堆栈中弹出外层堆栈的指针ESP及SS的值。这些做法与RET指令相似。例如,使用CS选择子的RPL,而不是由选择子标识的段的DPL,是为了返回到不在DPL给定特权级的一致代码段。若弹出的CS的选择子的RPL规定了一个内层特权级,则产生通用保护故障。需要注意的是,对于IRET指令,保存在当前堆栈中的返回地址中的选择子字段必须指向代码段描述符。而不能是系统段或门描述符。否则将引起通用保护故障。
对于提供出错代码的异常处理程序,必须先人为地从堆栈中弹出出错代码,然后再执行IRET指令,及出错代码不会自动被处理器弹出或取消。
中断返回指令IRET不仅能够用于由中断/异常引起的嵌套任务的返回,而且也适用于由段间调用指令CALL通过任务门引起的嵌套任务的返回,如前文所述,在执行通过任务门进行任务切换的段间调用指令CALL时,标志寄存器中的NT位被置为1,表示任务嵌套。而RET指令不能实现此功能。