异常和中断的分类
上一节把事件分成了异常和中断两大类。这一节把每一类再往下切一层。
切分的依据不是”事件本身像什么”,而是”处理完之后回到哪里”。 这个视角一旦立住,五个子类的名字就不必硬背了——它们是按返回地址的三种可能反推出来的分类。
机制
异常的三个子类
| 子类 | 什么时候发生 | 处理完之后 | 典型 |
|---|---|---|---|
| 故障 Fault | 指令执行过程中发现问题,问题可能被修复 | 重新执行当前这条指令 | 缺页、除零、段错误、非法操作码 |
| 自陷 Trap | 指令有意触发,本身就是为了进内核 | 执行下一条指令 | 系统调用 INT n / syscall、断点 INT 3、单步调试 |
| 终止 Abort | 硬件出了无法恢复的错 | 不返回,程序(甚至系统)终止 | 校验错、总线错、机器检查 |
三个子类恰好对应”返回地址”的三种可能:当前指令、下一条指令、不返回。 这就是本节开头说的反推。
故障为什么要重新执行当前指令,用缺页解释最清楚:MOV EAX, [ADDR] 执行到一半发现 ADDR 所在的页不在内存,于是触发缺页故障;操作系统把页调进来(OS 3.2.2),然后必须让这条 MOV 重来一遍——因为它上次根本没取到数。若返回到下一条,这次访存就永久丢失了。
| Fault | 能否修复后重执行 | 常见处理 |
|---|---|---|
| 缺页 | 通常能 | 调页后重执行 |
| 除零 | 通常不能自然修复 | 终止/抛异常 |
| 非法操作码 | 通常不能 | 终止,也可能软件模拟 |
| 保护异常 | 视原因而定 | 修复或终止 |
自陷为什么返回下一条:INT 0x80 的语义就是”我要进内核办件事”,办完了当然继续往下走。它的行为和 CALL 极像——区别只在于它同时切换了特权级(OS 1.3.1)。
这里有一个必须点破的不对称:故障和自陷都由当前指令引起,但保存的断点不同。
而 5.2.2 说过,PC 在取指周期就已经自增,所以硬件手里现成的那个值是”下一条”。于是:
- 自陷:直接存 PC 的当前值即可,硬件不用做额外的事
- 故障:必须把 PC 回退到当前指令的开头再保存——这需要硬件额外记住”当前指令有多长”或”当前指令的起始地址”
“故障要回退 PC”是本节最容易被忽略的一处实现代价,而它在变长指令集(4.1.4)上尤其麻烦:指令长度不定,回退多少字节不是常数,所以 x86 需要专门保存当前指令的起始地址。这是定长指令的又一个隐性好处。
中断的两个子类
| 子类 | 能否被屏蔽 | 走哪根线 | 典型 |
|---|---|---|---|
| 可屏蔽中断 | 能——CPU 关中断(IF=0)或设备被屏蔽时不响应 | INTR | 键盘、磁盘、打印机、时钟 |
| 不可屏蔽中断 NMI | 不能——任何情况下都响应 | NMI | 电源掉电、内存校验错、严重硬件故障 |
NMI 存在的理由是时间:掉电后靠电容供电只剩几毫秒,必须立刻保存现场,没有等待”开中断”的余地。所以它绕过 IF 这道总闸,直接进。
NMI 与可屏蔽中断走的是两根不同的物理引脚——这一点决定了它们的判优不在同一个队列里:NMI 的优先级永远高于任何可屏蔽中断,不需要参与排队。
完整分类表
中断(外中断)的返回位置只有一种:下一条指令。 因为它是在指令边界响应的,当前指令已经完整执行完了。
硬件与软件的分工
这张表在选择题里出现的频率极高,尤其是”下列哪些由硬件完成”这类问法。
| 阶段 | 做什么 | 谁做 |
|---|---|---|
| 检测 | 每条指令末尾查中断请求线;执行中检查异常条件 | 硬件 |
| 响应 | 关中断、保存断点(PC)、保存 PSW、转入口地址 | 硬件(中断隐指令) |
| 保存现场 | 保存通用寄存器 | 软件(服务程序开头) |
| 判断中断源 | 确定是哪个设备 / 哪种异常 | 硬件(向量方式)或软件(查询方式) |
| 具体处理 | 读扫描码、调页、打印错误…… | 软件 |
| 恢复现场 | 恢复通用寄存器 | 软件 |
| 返回 | IRET 恢复 PC 和 PSW、开中断 | 软件发起,硬件完成 |
分界线在”保存断点”与”保存现场”之间,这两个词只差一个字,含义完全不同:
断点 = PC + PSW,由硬件保存——不保存它,程序回不来 现场 = 通用寄存器,由软件保存——不保存它,程序回来了但数据乱了
为什么硬件不顺手把通用寄存器也存了? 因为不知道要存多少。中断服务程序可能只用两个寄存器,全存 32 个是浪费;而且不同 ISA 的寄存器数量不同,硬件把这件事交给软件,正是 ISA 抽象的一部分(4.3.4 里 caller/callee-saved 的划分是同一个思路:让软件按需决定存哪些)。
与操作系统的接口
跨到 OS 那边,这套分类原样复用,只是关注点变了:
| CO 关心 | OS 关心 |
|---|---|
| 硬件怎样夺走控制权 | 夺走之后由哪段内核代码接手 |
| 断点保存在哪 | 进程的上下文保存到 PCB 的哪个字段 |
| 故障要重执行 | 重执行前要把缺的页调进来 |
| 自陷返回下一条 | 系统调用号怎样分发到具体的服务例程 |
三个必须记住的对应:
- 系统调用 = 自陷类异常(OS 1.3.3)
- 缺页 = 故障类异常(OS 3.2.2)
- 时钟中断 = 可屏蔽中断,它是进程调度得以发生的硬件基础(OS 2.1.3)——没有时钟中断,一个死循环的用户程序就能永久占住 CPU
最后这一条值得停一下:抢占式调度之所以可能,完全是因为有一个 CPU 无法拒绝的外部事件在定期打断它。 这是”中断”这个机制对操作系统最根本的贡献,比 I/O 通知重要得多。
边界
层次辨析:故障 / 自陷 / 终止
判据是返回到哪:
- 故障 → 重新执行当前指令(缺页、除零)
- 自陷 → 执行下一条(系统调用、断点)
- 终止 → 不返回(硬件故障)
反推记法:三个子类就是”返回地址的三种可能”。
边界辨析:断点 / 现场
- 断点 = PC + PSW,硬件在中断隐指令中保存
- 现场 = 通用寄存器,软件在服务程序开头保存
高频选择题:“下列由硬件完成的是”——答关中断、保存断点、转入口地址,不含保存通用寄存器。
边界辨析:可屏蔽 / 不可屏蔽
- 可屏蔽(INTR):受 IF 和设备屏蔽位控制,可以被暂时挡住
- 不可屏蔽(NMI):任何情况下都响应,优先级最高,不参与排队
NMI 的理由是时间紧迫(掉电只剩几毫秒),不是”更重要”这种模糊说法。
边界辨析:自陷是异常,不是中断
系统调用由指令主动触发,内部、同步、可复现——三条判据全指向异常。
“主动触发”不构成把它算作中断的理由。中文里”软中断”这个叫法是历史遗留,容易误导。
对照速查
| 事件 | 类别 | 返回到 |
|---|---|---|
| 缺页 | 异常 - 故障 | 当前指令 |
| 除数为 0 | 异常 - 故障 | 当前指令 |
| 非法操作码 | 异常 - 故障 | 当前指令 |
| 系统调用 | 异常 - 自陷 | 下一条 |
断点 INT 3 | 异常 - 自陷 | 下一条 |
| 校验错 / 总线错 | 异常 - 终止 | 不返回 |
| 键盘、磁盘、时钟 | 中断 - 可屏蔽 | 下一条 |
| 掉电 | 中断 - NMI | 一般不返回 |
| 由硬件完成 | 由软件完成 |
|---|---|
| 检测、关中断 | 保存现场(通用寄存器) |
| 保存断点(PC、PSW) | 判断中断源(查询方式时) |
| 转中断服务程序入口 | 具体处理 |
| 判断中断源(向量方式时) | 恢复现场、发起 IRET |
考点
- 给事件判子类 + 返回位置:本节最稳定的题型,靠上面那张速查表。
- “下列哪些由硬件完成”:答关中断、保存断点、送入口地址。保存通用寄存器是软件做的。
- 故障与自陷的断点差异:故障存当前指令地址,自陷存下一条。
- NMI 的特点:不可屏蔽、优先级最高、走独立引脚、用于掉电等紧急故障。
- 系统调用的归类:异常中的自陷。
- 时钟中断与抢占式调度的关系:跨科高频,见 OS 2.1.3。
链接
- 🏠 返回总览:计算机组成原理第 5 章:中央处理器总览
- ⬅️ 上一节:5.5.1 异常和中断的基本概念
- ➡️ 下一节:5.5.3 异常和中断响应过程
- 🔗 缺页处理:OS 3.2.2 请求分页管理方式
- 🔗 系统调用:OS 1.3.3 系统调用
- 🔗 caller/callee-saved 的同构思路:4.3.4 过程调用
- 📖 名词库:第 5 章名词库