在计算机系统中,包括任务切换、时间更新、软件调试在内的很多功能都是依靠中断和异常机制实现的。
中断
中断通常是由CPU外部的输入输出设备(硬件)所触发的,供外部设备通知CPU“有事情需要处理”,因此又叫中断请求。
中断请求的目的是希望CPU暂时停止执行当前正在执行的程序,转去执行中断请求所对应的终端处理例程(Interrupt Service Routine,ISR)。
有些任务是不能被打断的,为了防止CPU在执行这些任务的时候被打断,可以通过执行CLI(clear interrupt)指令清除标志寄存器的IF位,使得此时的CPU不会被“打扰”
有个特殊的东西:一旦有不可屏蔽中断(Non-Maskable Interrupt,NMI)发生时,CPU要立即转去处理。【这种情况非常少】
异常
异常就是满足了CPU内部定义的一些特定条件而产生的 同步事件。
异常来源:
- 程序错误(比如除零)
- 特殊指令(INT3 ==》 断点异常)
- 机器检查异常:CPU执行指令期间检测到CPU内部或外部的硬件错误
异常的分类
- 错误类异常
导致错误异常的情况通常可以被纠正,而且一旦纠正后,程序可以无损失地恢复执行。(比如内存页错误(page fault))
CPU报告错误类异常时,CPU将其状态恢复成导致该异常的指令被执行之前的状态。而且在CPU转去执行异常处理程序前,在栈中保存的CS和EIP指针是指向导致异常的这条指令,所以,当异常处理程序返回继续执行时,CPU接下来被执行的第一条指令仍然是刚才导致异常的那条指令。【如果导致异常的情况还没有被消除,那么CPU会再次产生异常】 - 陷阱类异常
导致陷阱类异常的情况通常可以无损失地恢复执行。(比如INT3导致的断点异常就属于陷阱类异常)。
当CPU报告陷阱类异常时,导致该异常的指令已经执行完成,压入栈的CS和EIP的值是导致该异常的指令执行后接着要执行的下一条指令。【不一定是相邻的指令(比如异常的那一句是跳转指令)】 - 中止类异常
中止类异常主要用来报告严重的错误(比如硬件错误和系统表中包含非法值),这种异常是不允许恢复继续执行的。
比如:栈溢出等
中断/异常优先级
多个异常同时发生时,按照优先级处理:
异常分发
内核服务NtRaiseException是产生软件异常的主要方法,用户态代码可以通过RaiseException这个API来调用此内核服务。 NtRaiseException内部会调用KiRaiseException,KiRaiseException再调用内核函数KiDispatchException进行异常分发。
对于CPU级别的异常,CPU会通过IDT表寻找异常的处理函数入口(也就是KiTrapXX例程)。对于需要按异常流程分发的异常,KiTrapXX例程会调用CommonDispatchException准备必要的参数,然后调用KiDispatchException进行异常分发。