操作系统学习(十三) 、中断和异常
一、中断和异常
中断何和异常是指明系统、处理器或当前执行程序(或任务)的某处出现一个事件,该事件需要处理器进行处理。通常,这种事情会导致执行控制器被强迫从当前运行程序转移到被称为终端处理程序或异常处理程序的特殊软件函数或任务中。处理器响应中断或异常所采取的行动称为中断/异常服务(处理)。
通常中断发生在程序执行的随机时刻,以响应硬件发出的信号。系统硬件使用中断来处理外部事件,例如要求为外部设备提供服务。当然,软件也能通过执行 INT n 指令产生中断。
异常发生在处理器执行一条指令时,检测到一个出错条件时发生,例如被0除出错条件。处理器可以检测到各种出错条件,包括违反保护机制。页错误以及机器内部错误。对应用程序来说,80x86的中断和异常处理机制可以透明地处理发生的异常和中断事件。当收到一个中断或检测到一个异常时,处理器会自动把当前正在正在执行的程序或任务挂起,并开始运行中断或异常处理程序。当处理程序执行完毕,处理器就会恢复并继续执行被中断的程序或任务。被中断程序的恢复过程并不会失去程序执行的连贯性,除非从异常中恢复是不可能的或者中断异常导致当前运行程序被终止。
二、异常和中断向量
为了有助于处理异常和中断,每个需要被处理器进行特殊处理的处理器定义的异常和中断条件都被赋予了一个标识号,称为向量。处理器把赋予异常或中断的向量用作中断描述符表IDT中的一个索引号,来定位一个异常或中断的处理程序入口点位置。
允许的向量号范围是0到255.其中0到31保留用作80x86处理器定义的异常和中断,不过目前该范围内的向量号并非每个都已定义了功能,未定义功能的向量号将留在以后使用。
范围在32到255的向量号用于用户定义的中断。这些中断通常用于外部I/O设备,使得这些设备可以通过外部硬件中断机制向处理器发送中断。
三、中断源和异常源
(一)、中断源
处理器从两种地方接收终端:
- 外部(硬件产生)的中断。
- 软件产生的中断。
外部中断通过处理器芯片上两个引脚(INTR 和 NMI)接收。当引脚接收到外部发生的中断信号时,处理器就会从系统总线上读取外部中断控制器提供的中断向量号。当引脚 NMI 接收到信号时,就产生一个非屏蔽中断。它使用固定的中断向量号2。 任何通过处理器 INTR 引脚接收的外部中断都被称为可屏蔽硬件中断,包括中断向量号0到255。标志寄存器 EFLAGS 的 IF 标志可用来屏蔽所有这些硬件中断。
通过在指令操作数中提供中断向量号, INT n 指令可用于从软件中产生中断。例如,指令 INT 0x80 会执行Linux的系统系统中断调用中断0x80。向量0到255中的任何一个都可以用作INT指令的中断号。如果使用了处理器预先定义的 NMI向量,那么处理器对它的响应将与普通方式产生的该NMI中断不同,如果NMI的向量号2用于该INT指令,就会调用NMI的中断处理程序,但此时并不会激活处理器的NMI处理硬件(???)。
注意,EFLAGS中的IF标志位不能屏蔽使用INT执行从软件产生的中断。
(二)、异常源
处理器接收的异常也有两个来源:
- 处理器检测到的程序错误异常。
- 软件产生的异常。
在应用程序或操作系统执行期间,如果处理器检测到程序错误,就会产生一个或多个异常。80x86处理器为其检测到的每一个异常定义了一个向量。异常可以被细分为故障
、陷阱
和中止
。
指令INTO、INT 3 和 BOUND 指令可以用来从软件中产生异常。这些指令可对指令流中指定点执行的特殊异常条件进行检查。例如,INT 3 指令会产生一个断点异常。
INT n 指令可用于在软件中模拟指定的异常,但有一个限制。如果INT指令中的操作数n是80x86异常的向量号之一,那么处理器将为该向量号产生一个中断,该中断就会去执行与该向量有关的异常处理程序。但是,因为实际上是一个中断,因此处理器并不会把一个错误号压入栈,即使硬件产生的该向量相关的中断通常会产生一个错误码。对于那些会产生错误码的异常,异常的处理程序会试图从栈上弹出错误码。因此,如果使用INT指令来模拟产生一个异常,处理程序则会把EIP(正好处于缺少的错误码位置处)弹出栈 ,从而会造成返回位置错误。
四、异常的分类
根据异常被报告的方式以及导致异常的指令是否能够被重新执行,异常可被细分为故障(Fault)、陷阱(Trap)和中止(Abort)。
- Fault 是一种通常可以被纠正的异常,并且一旦被纠正程序就可以继续运行。当出现一个Fault,处理器会把机器的状态恢复到产生Fault的指令之前的状态。因此异常处理程序的返回地址会指向产生Fault的指令,而不是其后面一条指令。因此在返回后产生Fault的指令将被重新执行。
- Trap 是一个引起陷阱的指令被执行后立刻会报告的异常。Trap也能够让程序或任务连贯地执行。Trap处理程序的返回地址指向引起陷阱指令的随后一条指令,因此在返回后会执行下一条指令。
- Abort 是一种不会总是报告导致异常的指令的精确位置的异常,并且不允许导致异常的程序重新继续执行。Abort用于报告严重错误,例如硬件错误以及系统表中存在不一致性或非法值。
五、程序或任务的重新执行
为了让程序或任务在一个异常或中断处理完之后能重新恢复执行,除了中止之外的所有异常都能报告精确的指令位置,并且所有中断保证是在指令边界上发生。
- 对于故障类异常,处理器产生异常时保存的返回指针指向出错指令。因为,当程序或任务在故障处理程序返回后重新开始执行时,原出错指令会被重新执行。重新执行引发出错的指令通常用于处理访问指令操作数受阻止的情况。Fault最常见的一个例子是页面故障(Page-Fault)异常。当程序引用不在内存中页面上的一个操作数时就会出现这种异常。当页故障异常发生时,异常处理程序可以把该页面加载到内存中并通过重新执行出错指令来恢复程序运行。为了确保重新执行对于当前执行程序具有透明性,处理器会保存必要的寄存器和堆栈指针信息,以使得自己能够返回到执行出错指令之前的状态。
- 对于陷阱Trap类异常,处理器产生异常时保存的返回指针指向引起陷阱操作的后一条指令。如果在一条执行控制转移的指令执行期间检测到一个Trap,则返回指令指针会反映出控制的转移情况。例如,如果在执行JMP指令时检测到一个Trap异常,那么返回指令指针会指向JMP指令的目标位置,而非指向JMP指令随后的一条指令。
- 中止Abort类异常不支持可靠地重新执行程序或任务。中止异常的处理程序通常用来收集异常发生时有关处理器状态的诊断信息,并且尽可能恰当地关闭程序和系统。
中断会严格地支持被中断程序的重新执行而不会失去任何连贯性。中断所保存的返回指令指针指向处理器获取中断时将要执行的下一条指令的边界处。如果刚执行的指令有一个重复前缀,则中断会在当前重复结束并且寄存器已为下一次重复操作设置好时发生。
六、开启和禁止中断
标志寄存器EFLAGS的中断允许标志IT能够禁止为处理器INTR引脚上收到的可屏蔽硬件中断提供服务。当IF=0时,处理器禁止发送INTR引脚的中断;当IF=1时,则发送到INTR引脚的中断信号会被处理器进行处理。
IF标志并不影响发送到NMI引脚的非屏蔽中断,也不影响处理器产生的异常。如同EFLAGS中的其它标志一样,处理器在相应硬件复位操作时会清楚IF标志(IF=0)。
IF标志可以使用指令STI和CLI来设置或清除。只有当程序的CPL<=IOPL 时才可执行这两条指令,否则将引发一般保护性异常。IF标志也会受以下操作影响:
- PUSHF 指令会把EFLAGS内容存到栈中,并且可以在那里被修改。而POPF指令可用于把已经被修改过的标志内容放入EFLAGS寄存器中。
- 任务切换、POPF 和 IRET 指令会加载EFLAGS寄存器。因此,它们可用来修改IF标志。
- 当通过中断门处理一个中断时,IF标志会被自动清除(复位),从而会禁止可屏蔽硬件中断。但如果是通过陷阱门来处理一个中断,则IF标志不会被复位。
七、异常和中断的优先级
如果在一条指令边界有多个异常或中断等待处理时,处理器会按规定的次序躲他们进行处理。处理器会首先处理最高优先级类的异常或中断。低优先的异常会被丢弃,而低优先级的中断则会保持等待。当中断处理程序返回到产生异常和/或中断的程序或任务时,被丢弃的异常会重新发生。
优先级 | 说明 |
---|---|
1(最高) | 硬件复位: RESET |
2 | 任务切换陷阱: TSS中设置了T标志 |
3 | 外部硬件介入 |
4 | 前一指令陷阱: 断点、调试陷阱异常 |
5 | 外部中断: NMI中断、可屏蔽硬件中断 |
6 | 代码断点错误 |
7 | 取下一条指令错误: 违反代码段限长、代码页错误 |
8 | 下一条指令译码错误: 指令长度>15字节、无效操作码、协处理器不存在 |
9(最低) | 执行指令错误: 溢出、边界检查、无效TSS、段不存在、堆栈错误、一般保护、数据页、对其检查、浮点异常。 |