Chapter 4.6 - 中断和异常处理
Created by : Mr Dk.
2019 / 07 / 28 22:35
Nanjing, Jiangsu, China
4.6 中断和异常处理
- 中断:Interrupt
- 异常:Exception
指明系统、处理器或当前程序某处出现一个时间,需要处理器进行处理。强迫控制权从运行程序转移到中断处理程序 (Interrupt handler) 或异常处理程序 (Exception handler) 中。处理器响应中断或异常所采取的行动被称为中断 / 异常服务 (处理)。中断通常发生在程序执行的随机时刻,以响应硬件发出的信号,但软件也能通过指令产生中断。
异常发生在处理器执行指令时,检测到一个出错条件的发生。对于应用程序,CPU 透明地处理发生的中断和异常事件。检测到中断时:
- CPU 自动把正在执行的程序挂起
- 开始运行中断处理或异常处理程序
- CPU 恢复被中断的程序
被中断程序的恢复过程不会失去程序执行的连贯性。
4.6.1 异常和中断向量
CPU 定义的每个异常和中断都被赋予了一个标识号,称为向量 vector,被用于中断描述符表 IDT 中的索引号,定位处理程序入口点的位置。允许的向量号范围 - 0-255
- 0 到 31 保留用作 CPU 定义的异常和中断
- 32 到 255 用于用户定义的中断
4.6.2 中断源和异常源
4.6.2.1 中断源
处理器从两种地方接收中断
- 硬件产生的中断
- 软件产生的中断
硬件中断通过 CPU 芯片上的 INTR
和 NMI
引脚接收:
- INTR 接收到外部中断信号时,CPU 会从系统总线上读取外部中断控制器提供的向量号
- NMI 接收到信号时,将产生一个不可屏蔽中断,向量号固定为
2
标志寄存器 EFLAGS 中的 IF 标志可用来屏蔽硬件中断。通过在 INT 指令中提供操作数,软件也可以产生中断,0-255 中的任何一个都可以用作 INT 指令的中断号。通过软件产生的 NMI 中断只会调用 NMI 的中断处理程序,而不会激活 CPU 中的 NMI 处理硬件。EFLAGS 中的 IF 标志不能屏蔽软件产生的中断。
4.6.2.2 异常源
- 处理器检测到的程序异常
- 软件产生的异常
在程序或 OS 运行期间,如果 CPU 检测到了程序错误,就会产生异常:
- 故障 - faults
- 陷阱 - traps
- 中止 - aborts
有几个向量号用于在软件中产生异常。
4.6.3 异常分类
- Faults:一种可以被纠正的异常,一旦被纠正,程序就可以继续运行;异常处理程序的返回地址是引发 fault 的指令,返回后,产生 fault 的指令将被重新执行
- Trap:引起陷阱的指令被执行后会立刻报告异常,异常处理程序的返回地址指向引起陷阱指令的随后一条指令
- Abort:不允许导致异常的程序重新继续执行,用于报告严重错误
4.6.4 程序或任务的重新执行
所有中断保证是在指令边界上发生。对于故障类异常,异常处理返回地址指向出错指令,原出错指令会被重新执行,通常用于处理访问指令操作数受阻的情况,比如 page-fault。为了确保重新执行对于当前执行程序具有透明性,CPU 会保存必要的寄存器和堆栈信息
对于陷阱类异常,异常处理返回地址指向引起陷阱操作的后一条指令。如果跳转指令引起异常,则返回地址是跳转之后的第一条指令。
对于中止类异常,不支持可靠地重新执行程序或任务。
4.6.5 开启和禁止中断
EFLAGS 中的 IF 能够为 CPU INTR 引脚上收到的 可屏蔽硬件中断 提供服务。IF 标志不影响发送到 NMI 引脚的非屏蔽中断,也不影响 CPU 产生的异常。
4.6.6 异常和中断的优先级
如果一条指令边界有多个异常或中断等待处理时,CPU 会首先处理最高优先级的异常或中断:
- 低优先级的异常会被丢弃
- 低优先级的中断会保持等待
- 当中断处理程序返回时,被丢弃的异常会重新发生
4.6.7 中断描述符表
与 GDT 和 LDT 表类似,IDT 也是一个由 8B 的中断描述符组成的数组。CPU 将向量号 × 8,成为 IDT 中的索引值。最多有 256 个描述符,其中可以有描述符不存在。IDT 可以驻留在线性地址空间的任何地方,CPU 通过 IDTR 寄存器来定位 IDT 的位置。IDTR 包含:
- 32-bit IDT 线性基地址,应该对齐在 8B 边界上
- 16-bit 表限长 - 以字节为单位的 IDT 表长度
如果引用的描述符超过了 IDT 的界限,CPU 会产生一个一般保护性异常。
4.6.8 IDT 描述符
IDT 表中可以存放三种类型的门描述符:
- 中断门描述符 - Interrupt gate
- 陷阱门描述符 - Trap gate
- 任务门描述符 - Task gate
中断门和陷阱门含有一个长指针 (段选择符 + 偏移量),CPU 使用该长指针将程序执行权转移到代码段中的中断或异常处理过程中。
任务门描述符的格式与 GDT 和 LDT 中的任务门格式相同,包含 TSS 段的选择符,该任务用于处理异常或中断。
4.6.9 异常与中断处理
CPU 使用向量作为 IDT 表中的索引:
- 如果索引值指向中断门或陷阱门,则采用与 CALL 指令调用门类似地方法调用处理过程
- 如果索引值指向任务门,则采用与 CALL 指令操作任务门类似的方法进行任务切换,执行处理任务
门中的段选择符指向 GDT 或当前 LDT 中的可执行代码段描述符,门描述符中的偏移量指向异常或中断处理过程的入口处:
- 如果异常处理程序在高特权级上执行,需要切换堆栈
- CPU 从当前任务的 TSS 段中获得异常或中断处理程序所使用的堆栈
- CPU 将被中断任务的栈选择符和栈指针压入新栈
- CPU 将 EFLAGS、CS、EIP 压入新栈
- 如果异常产生错误号,则错误号也被压入新栈
- 如果异常处理程序在同一特权级上运行
- CPU 将 EFLAGS、CS、EIP 保存在当前堆栈上
- 如果异常产生错误号,则错误号也会被压入新栈中
中断返回时,使用 IRET 指令,执行处理上述操作的逆过程。
4.6.9.1 异常和中断处理过程的保护
与通过调用门调用普通过程类似,CPU 不允许将控制转移到比 CPL 特权级更低的代码段中,否则产生一个一般保护性异常。中断和异常向量没有 RPL,因此不需检查。若异常和中断通过指令产生,CPU 才会检查中断或陷阱门中的 DPL,对于硬件产生的异常,CPU 忽略 DPL。
4.6.9.2 异常或中断处理过程的标志使用方式
通过中断门或陷阱门访问一个异常或中断处理过程时,CPU 会把 EFLAGS 内容保存在堆栈上之后,清除 TF 标志,防止指令跟踪影响中断响应。随后 IRET 会恢复原 TF 标志。
通过中断门访问异常或中断处理过程时,CPU 复位 IF 标志,防止其它中断干扰当前中断处理过;IRET 会恢复 IF 标志。
通过陷阱门访问处理过程不会影响 IF 标志。
4.6.9.3 执行中断处理过程的任务
当通过 IDT 表中的任务门访问处理过程时,将会导致任务切换。因此可以在一个专用的任务中执行中断和异常处理过程。
4.6.10 中断处理任务
使用单独的任务来处理异常和中断有如下好处:
- 被中断程序或任务的完整上下文将被自动保存
- 新的 TSS 可以允许处理过程使用新特权级 0 的堆栈,防止系统崩溃
- 通过使用单独的 LDT 给中断或异常处理任务独立的地址空间,可以与其它任务隔离
然而,任务切换时必须对大量机器状态进行保存,导致比使用中断门的响应速度慢。IDT 中的任务门会引用 GDT 中的 TSS 描述符,切换处理过程任务的过程与普通任务切换过程相同。
被中断任务的反向链接会被保存在中断处理过程任务 TSS 的前一任务链接字段中。如果异常产生错误码,则错误码会被复制到新任务堆栈上。
4.6.11 错误码
当异常条件与一个特定的段相关时,CPU 会将一个错误码压入异常处理的堆栈中:
- 段选择符索引
- EXT (External event) 标志 - 表示执行程序以外的时间造成了异常
- IDT Descriptor location 标志 - 错误码索引指向 IDT 中的描述符或 GDT/LDT 中的描述符
- TI - GDT/LDT 选择标志
空错误码表示错误不是由某个特定的段造成,可能是 OS 中引用了空段的描述符。
页故障 (Page-fault) 的错误码与上述不同,只有最低 3 个 bit 有用:
- P - 异常是由于页面不存在引起的,还是由于违反访问特权引起的
- W/R - 异常是由于读操作引起的,还是写操作引起的
- U/S - 发生异常时,CPU 执行的代码级别 - 普通用户/超级用户
另外,CPU 将引起页面故障的线性地址存放在 CR2 中。异常处理程序利用该地址来定义相关的页目录和页表项。注意,IRET 不会自动将错误码弹出堆栈,因此中断处理程序返回之前必须清除堆栈上的错误码。某些异常会把错误码自动保存到处理过程的堆栈中,但外部硬件中断或者程序执行 INT n
指令产生的异常不会把错误码压入堆栈中。