异常

  1. 异常是一个硬件和软件组合在一起的处理过程
    • 异常的发生捕捉,是在硬件层面完成的
    • 异常的处理,是在软件层面完成的
  2. 计算机会为每一种可能发生的异常,分配一个异常代码(Exception Number),别称中断向量(Interrupt Vector)
  3. 异常发生的时候,通常是CPU检测到一个特殊的信号
    • 在组成原理里面,一般叫作发生了一个事件(Event),CPU在检测到事件的时候,就已经拿到了对应的异常代码
  4. 异常代码
    • IO发出的信号的异常代码,是由操作系统来分配,即由软件来设定
    • 加法溢出这样的异常代码,是由CPU预分配的,即由硬件来设定
  5. 拿到异常代码后,CPU会触发异常处理流程
    • 计算机在内存里,会保留一个异常表(Exception Table),别称中断向量表(Interrupt Vector Table)
    • 存放的是不同的异常代码对应的异常处理程序所在的地址
    • CPU拿到异常代码后,会先把当前程序的执行现场(CPU当前运行程序用到的所有寄存器),保存到程序栈里面
    • 然后根据异常代码查询,找到对应的异常处理程序,最后把后续指令执行的指挥权,交给这个异常处理程序
  6. 异常可以由硬件触发,也可以由软件触发

分类

  1. 中断(Interrupt)
    • 程序执行到一半的时候,被打断了,该打断执行的信号,来自于CPU外部的IO设备
    • 在键盘上按下一个按键,就会触发一个相应的信号到达CPU
      • CPU里面某个开关的值发生了变化,即触发了一个中断类型的异常
  2. 陷阱(Trap)
    • 陷阱是程序主动触发的异常
    • 断点
      • 当程序的指令执行到某个位置时,会掉入这个陷阱中,然后,对应的异常处理程序会来处理
    • 系统调用
      • 当应用程序调用系统调用时,会从程序的用户态切换到内核态
      • 应用程序通过系统调用去读取文件、创建进程,也是通过触发一次陷阱来进行的
        • 这是因为用户态的应用程序没有权限来做这些事情,要把对应的流程转交给有权限的『异常处理程序』来进行
  3. 故障(Fault)
    • 与陷阱的区别:陷阱是程序刻意触发的,而故障则不是
    • 程序进行加法计算时发生了溢出,属于故障类型的异常
    • 故障和陷阱、中断的一个重要区别
      • 故障在异常程序处理完成后,仍然回来处理当前指令,而不是去执行程序的下一条指令
      • 因为当前指令因为故障的原因并没有成功执行完成,需要重新执行一次(抢救一下!)
  4. 中止(Abort)
    • 中止是故障的一种特殊情况
    • CPU遇到故障,但恢复不过来的时候,程序不得不中止(退出程序执行)
类型 原因 示例 触发时机 处理后操作
中断 IO设备信号 用户键盘输入 异步 下一条指令
陷阱 程序刻意触发 程序进行系统调用(用户态、内核态切换) 同步 下一条指令
故障 程序执行出错 程序加载的缺页错误 同步 当前指令
中止 故障无法恢复 ECC内存校验失败 同步 退出程序
  1. 异步 + 同步
    • 中断异常的信号来自于系统外部,而不是在程序的执行过程中,因此称为异步类型的异常
    • 陷阱故障以及中止类型的异常,是在程序执行的过程中发生的,因此称为同步类型的异常
  2. 处理异常的过程中,无论是异步的中断,还是同步的陷阱和故障,都采用统一的处理流程
    • 保存现场 -> 异常代码查询 -> 异常处理程序调用
    • 中止类型的异常,是故障类型异常的一种特殊情况
      • 中止异常发生时,发现没有异常处理程序能够处理这种异常,程序不得不进入中止状态,退出当前程序的执行

处理 – 上下文切换

  1. 在实际的异常处理流程执行之前,CPU需要去做一次『保存现场』的操作
    • 保存现场的操作,和函数调用的过程非常类似
    • 切换到异常处理程序的时候,好像去调用一个异常处理函数,指令的控制权被切换到另一个函数里面
    • 因此要把当前正在执行的指令去压栈,这样才能在异常处理程序执行完成之后,重新回到当前指令继续往下执行
  2. 切换到异常处理程序,比函数调用,是更复杂一些
    • 因为异常情况通常发生在程序正常执行的预期之外,如中断、故障发生的时候
      • 因此除了本来程序压栈要做的事情之外,还需要把CPU当前运行程序用到的所有寄存器,都放到里面
        • 典型案例:条件码寄存器里面的内容
    • 陷阱:涉及程序指令在用户态内核态之间的切换
      • 压栈的时候,对应的数据是压到内核栈里,而不是程序栈
    • 故障:在异常处理程序执行完成之后,从栈里返回出来,继续执行的不是顺序的下一条指令,而是故障发生的当前指令
      • 因为当前指令因为故障没有正常执行成功,必须重新执行一次
  3. 异常处理流程,不像顺序执行的指令间的函数调用关系,而是更像两个不同的独立进程之间在CPU层面的切换
    • 因此这个过程称为上下文切换(Context Switch)

参考资料

深入浅出计算机组成原理