计算机组成 -- 异常
异常
- 异常是一个硬件和软件组合在一起的处理过程
- 异常的发生和捕捉,是在硬件层面完成的
- 异常的处理,是在软件层面完成的
- 计算机会为每一种可能发生的异常,分配一个异常代码(Exception Number),别称中断向量(Interrupt Vector)
- 异常发生的时候,通常是CPU检测到一个特殊的信号
- 在组成原理里面,一般叫作发生了一个事件(Event),CPU在检测到事件的时候,就已经拿到了对应的异常代码
- 异常代码
- IO发出的信号的异常代码,是由操作系统来分配,即由软件来设定
- 像加法溢出这样的异常代码,是由CPU预分配的,即由硬件来设定
- 拿到异常代码后,CPU会触发异常处理流程
- 计算机在内存里,会保留一个异常表(Exception Table),别称中断向量表(Interrupt Vector Table)
- 存放的是不同的异常代码对应的异常处理程序所在的地址
- CPU拿到异常代码后,会先把当前程序的执行现场(CPU当前运行程序用到的所有寄存器),保存到程序栈里面
- 然后根据异常代码查询,找到对应的异常处理程序,最后把后续指令执行的指挥权,交给这个异常处理程序
- 异常可以由硬件触发,也可以由软件触发
分类
- 中断(Interrupt)
- 程序执行到一半的时候,被打断了,该打断执行的信号,来自于CPU外部的IO设备
- 在键盘上按下一个按键,就会触发一个相应的信号到达CPU
- CPU里面某个开关的值发生了变化,即触发了一个中断类型的异常
- 陷阱(Trap)
- 陷阱是程序主动触发的异常
- 断点
- 当程序的指令执行到某个位置时,会掉入这个陷阱中,然后,对应的异常处理程序会来处理
- 系统调用
- 当应用程序调用系统调用时,会从程序的用户态切换到内核态
- 应用程序通过系统调用去读取文件、创建进程,也是通过触发一次陷阱来进行的
- 这是因为用户态的应用程序没有权限来做这些事情,要把对应的流程转交给有权限的『异常处理程序』来进行
- 故障(Fault)
- 与陷阱的区别:陷阱是程序刻意触发的,而故障则不是
- 程序进行加法计算时发生了溢出,属于故障类型的异常
- 故障和陷阱、中断的一个重要区别
- 故障在异常程序处理完成后,仍然回来处理当前指令,而不是去执行程序的下一条指令
- 因为当前指令因为故障的原因并没有成功执行完成,需要重新执行一次(抢救一下!)
- 中止(Abort)
- 中止是故障的一种特殊情况
- 当CPU遇到故障,但恢复不过来的时候,程序不得不中止(退出程序执行)
类型 | 原因 | 示例 | 触发时机 | 处理后操作 |
---|---|---|---|---|
中断 | IO设备信号 | 用户键盘输入 | 异步 | 下一条指令 |
陷阱 | 程序刻意触发 | 程序进行系统调用(用户态、内核态切换) | 同步 | 下一条指令 |
故障 | 程序执行出错 | 程序加载的缺页错误 | 同步 | 当前指令 |
中止 | 故障无法恢复 | ECC内存校验失败 | 同步 | 退出程序 |
- 异步 + 同步
- 中断异常的信号来自于系统外部,而不是在程序的执行过程中,因此称为异步类型的异常
- 陷阱、故障以及中止类型的异常,是在程序执行的过程中发生的,因此称为同步类型的异常
- 在处理异常的过程中,无论是异步的中断,还是同步的陷阱和故障,都采用统一的处理流程
- 保存现场 -> 异常代码查询 -> 异常处理程序调用
- 中止类型的异常,是故障类型异常的一种特殊情况
- 当中止异常发生时,发现没有异常处理程序能够处理这种异常,程序不得不进入中止状态,退出当前程序的执行
处理 – 上下文切换
- 在实际的异常处理流程执行之前,CPU需要去做一次『保存现场』的操作
- 保存现场的操作,和函数调用的过程非常类似
- 切换到异常处理程序的时候,好像去调用一个异常处理函数,指令的控制权被切换到另一个函数里面
- 因此要把当前正在执行的指令去压栈,这样才能在异常处理程序执行完成之后,重新回到当前指令继续往下执行
- 切换到异常处理程序,比函数调用,是更复杂一些
- 因为异常情况通常发生在程序正常执行的预期之外,如中断、故障发生的时候
- 因此除了本来程序压栈要做的事情之外,还需要把CPU当前运行程序用到的所有寄存器,都放到栈里面
- 典型案例:条件码寄存器里面的内容
- 因此除了本来程序压栈要做的事情之外,还需要把CPU当前运行程序用到的所有寄存器,都放到栈里面
- 陷阱:涉及程序指令在用户态和内核态之间的切换
- 在压栈的时候,对应的数据是压到内核栈里,而不是程序栈
- 故障:在异常处理程序执行完成之后,从栈里返回出来,继续执行的不是顺序的下一条指令,而是故障发生的当前指令
- 因为当前指令因为故障没有正常执行成功,必须重新执行一次
- 因为异常情况通常发生在程序正常执行的预期之外,如中断、故障发生的时候
- 异常处理流程,不像顺序执行的指令间的函数调用关系,而是更像两个不同的独立进程之间在CPU层面的切换
- 因此这个过程称为上下文切换(Context Switch)
参考资料
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.