计算机组成原理学习笔记——异常

186 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

异常

理想上,我们的程序都是自动且正常运行的,程序和指令都是一条条的顺序执行。我们不需要通过外设给程序任何输入。但是现实中,程序在执行指令时,还要和外部的输入输出通信,有时也会遇到各种异常,比如栈溢出。

异常可以分为来自硬件层面的和来自软件层面的。

硬件层面的异常,比如两个数相加会遇到算术溢出,比如按下鼠标或者键盘发送信号到CPU,CPU需要去执行现有流程之外的指令。

软件层面的异常,比如应用程序进行系统调用(用户态到内核态)。

有关异常,他的发生和捕捉,是由硬件完成。他的处理,是由软件来完成

计算机会为每一种异常分配一个异常代码(也叫中断向量)——查询相应异常处理程序的入口。像I/O发出的信号的异常代码,由操作系统(软件)分配。像加法溢出的异常代码,由硬件来分配。

异常发生时,通常是CPU检测到了一个特殊信号,这个信号我们一般叫做发生了一个事件。

CPU在检测到事件的时候,也拿到了异常代码(中断向量)。

发生异常,CPU拿到异常代码后,CPU就会触发异常处理流程。内存中,会有一个异常表(中断向量表),这个表存放的是不同异常代码对应的异常处理程序的地址。

拿到异常代码之后,CPU把当前的程序的执行现场,保存到程序栈中,然后根据异常代码查询并且找到对应的异常处理程序,然后把指令执行的指挥权交给异常处理程序。

Snipaste_2022-04-01_11-20-46.jpg

异常的分类

  1. 中断,程序执行到一半的时候,被打断了。而这个打断的信号,来自于CPU外部的I/O设备。

  2. 陷阱,我们在程序中设置一个“陷阱”,当程序走到这里的时候,就掉入了这个陷阱中。然后对应的异常处理程序来处理陷阱中的异常。

  3. 故障,我们程序在执行中,加法发生了溢出,或者栈溢出,这类异常就是故障。

    区别于陷阱和中断的是,故障的异常处理程序处理完之后,仍然要执行当前的指令,而不是当前指令的下一条指令。因为发生故障时当前指令并没有成功执行,所以要重新执行。

  4. 中止,CPU遇到故障,恢复不过来,程序不得不中止。

异常的处理:上下文切换

之前我们提到在执行异常处理程序之前,CPU要去“保存现场”,然后再去执行异常处理程序。这个过程有点像函数调用,不过相比于函数调用,切换到异常处理程序还有更复杂的几点。

  1. 异常往往发生在程序正常执行之外。比如中断、故障发生时。所以我们不仅要把程序压栈,还要把所有用到的寄存器都放到栈中。
  2. 像陷阱这样的异常,涉及到用户态到内核态的切换。压栈时,对应的数据要压到内核栈中。
  3. 像故障这样的异常,异常处理程序执行完之后,不是顺序执行下一条,而是执行当前故障发生的指令。

所以,相比于函数调用的过程,异常处理流程更像是两个独立进程之间在CPU层面的切换,我们叫这个过程为上下文切换