异常
每日一言
I was all dried up. Yes, nothing could satisfy me anymore. I was but a shell of a man. An unspeakable emptiness grew deep within my heart. I became frustrated, sad, hollow. The gapping hole inside of me was growing larger everyday. Slowly consuming me from the inside out. At least until that day came. – Excalibur
from Soul Eater
控制流
从给处理器加电开始,直到你断电为止,程序计数器假设一个值的序列
$$
(a_{0}, a_{1},\cdots, a_{n - 1})
$$
其中,每个$a_{k}$是某个相应的指令$I_{k}$的地址。每次从$a_{k}$到$a_{k+1}$的过渡称为控制转移(control transfer)。这样的控制转移序列叫做处理器的控制流(flow of control或control flow)。
异常
异常(exception)就是控制流中的突变,用来响应处理器的状态中的某些变化。其大致流程如下:
对于事件,我们的定义如下:事件是处理器发生的状态变化。当处理器中的某些状态变化时,就称之为发生了事件。
在任何时候,当处理器检测到有事件发生时,他就会通过异常表(一种跳转表),进行一个间接过程调用(异常),到一个专门用来处理这类事件的操作系统子程序(异常处理程序(exception handler))。
当异常处理程序完成处理后,根据引起异常的事件的类型,会发生以下三种情况中的一种:
- 处理程序将控制返回给当前指令$I_{curr}$,即当前事件发生时正在执行的指令。
- 处理程序将控制返回给下一指令$I_{next}$,如果没有异常将会执行的下一条指令。
- 处理程序终止被中断的程序。
异常处理
系统中可能的每种异常类型都分配了一个唯一的非负整数的异常号(exception number),其中一些号码是由处理器的设计者分配的,其他号码是由操作系统内核(操作系统常驻内存的部分)的设计者来分配的。
- 处理器设计者分配:被0除、缺页、内存访问违例、断点以及算数运算溢出
- 内核设计者分配:系统调用、外部I/O设备的信号
当系统启动时,操作系统分配初始化异常表,使得 表目k包含异常k的处理程序的地址,结构如下:
在系统运行时,处理器检测到发生了一个事件,并且确定了对应的异常号k后,通过异常表以及异常号来确定异常处理程序的地址。异常表的起始地址放在一个叫做异常表基地址寄存器,的特殊CPU寄存器中。
异常与控制流的同异:
- 相同:
- 都会将返回地址压入栈中
- 不同:
- 异常还会将一些额外的处理器状态压到栈里。
- 如果控制从用户程序转移到内核,那么所有这些项目都会被压到内核栈中,而不是用户栈。
- 异常处理程序运行在内核模式下,意味着它们对所有的系统资源都有完全的访问权限。
异常的类别
中断
中断是异步发生的,是来自处理器外部的I/O设别的信号的结果。硬件中断不是由任何一条专门的指令造成的,从这个意义上来说他是异步的。硬件中断的异常处理程序常常称为中断处理程序。
陷阱和系统调用
陷阱是有意的异常,是执行一条指令的结果。就像中断处理程序一样,陷阱处理程序将控制返回到下一条指令。陷阱最重要的用途是在用户程序和内核之间提供一个像过程一样的接口,叫系统调用。如syscall。
故障
故障由错误情况引起,它可能能够被故障处理程序修正。当故障发生时,处理器将控制转移给故障处理程序。如果处理程序能够修正这个错误情况,他就将控制返回到引起故障的指令,从而重新执行它。否则,处理程序返回到内核中的abort例程,abort例程会终止引起故障的应用程序。
终止
终止是不可恢复的致命错误造成的结果,通常是一些硬件错误。终止处理程序从不将控制返回给应用程序,直接abort该应用程序。