1 概览
完好的程序都满足以下特征
自动运行
我们的程序和指令都是一条条顺序执行,不需要通过键盘或者网络给这个程序任何输入
正常运行
没有遇到计算溢出之类的程序错误。
不过,现实的软件世界可没有这么简单
程序不仅是简单的执行指令,更多的还需要和外部的输入输出打交道
程序在执行过程中,还会遇到各种异常情况,比如除以0、 溢出,甚至我们自己也可以让程序抛出异常。
遇到这些情况,计算机是怎么运转的呢,也就是说,计算机究竟是如何处理异常的
2 异常
2.1 软件异常和硬件异常
软件异常指软件开发中遇到的异常,而硬件异常是和系统、软件相关的硬件异常
硬件异常有的来自硬件的也有来自软件层面的。
比如,硬件层面上,当两个数相加遇到算术溢出。软件层面,程序进行了一个系统调用,也是通过异常来实现的。
2.2异常代码
计算机会为每一种可能会发生的异常,分配一个异常代码(Exception Number)
异常代码也叫作中断向量(Interrupt Vector)。
异常发生的时候,通常是CPU检测到了一个特殊的信号。
比如
你按下键盘上的按键,输入设备就会给CPU发一个信号
正在执行的指令发生了加法溢出,同样,我们可以有一个进位溢出的信号
这些信号呢,在组成原理,一般叫发生了一个事件(Event)
CPU在检测到事件的时候,其实也就拿到了对应的异常代码。
这些异常代码里
I/O发出的信号的异常代码,是由操作系统来分配的,也就是由软件来设定的
像加法溢出这样的异常代码,则是由CPU预先分配好的,也就是由硬件来分配的. 这又是另一个软件和硬件共同组合来处理异常的过程
拿到异常代码之后,CPU就会触发异常处理的流程
计算机在内存里,会保留一个异常表 (Exception Table)。
也叫中断向量表(Interrupt Vector Table),好和上面的中断向量对应起来。
这个异常表有点儿像我们在之前的GOT表,存放的是不同的异常代码对应的异常处理程序(Exception Handler)所在的地址
3异常类型
3.1 中断
顾名思义,就是程序在执行到一半的时候,被打断了。这个打断执行的信号,来自于CPU外部的I/O设备。
你在键盘上按下一个按键,就会对应触发一个 相应的信号到达CPU里面。CPU里面某个开关的值发生了变化,也就触发了一个中断类型的异常
3.2 陷阱
程序员“故意“主动触发的异常。就好像你在程序里面打了一个断点,这个断点就是设下的一个"陷阱"。
当程序的指令执行到这个位置的时候,就掉到了这个陷阱当中。然后,对应的异常处理程序就会来处理这个"陷阱"当中的猎物。
最常见的一类陷阱,应用程序调用系统调用的时候,也就是从用户态切换到内核态的时候。
可以用Linux下的time指令,去查看一个程序运行实际花费的时间,里面有在用户态花费的时间(user time),也有在内核态发生的时间 (system time)。
应用程序通过系统调用去读取文件、创建进程,其实也是通过触发一次陷阱来进行的。这是因为用户态的应用程序没有权限来做这些事情,需要把对应的流程转交给有权限的异常处理程序来进行。
3.3 故障
陷阱是我们开发程序的时候刻意触发的异常,而故障通常不是。
比如,我们在程序执行的过程中,进行加法计算发生了溢出,其实就是故障类型的异常。
这个异常不是我们在开发的时候计划内的,也一样需要有对应的异常处理程序去处理。
故障和陷阱、中断的重要区别
故障在异常程序处理完成之后,仍然回来处理当前的指 令,而不是去执行程序中的下一条指令。
因为当前的指令因为故障的原因并没有成功执行完成。