计算机组成
9 中断和异常
9.4 中断的处理过程
那我们现在可以放心大胆地进行运算了。算,算,算,一旦遇到了一个异常情况,我们就根据这个异常情况的类型,去查找这个手册最前面的表格。假如我们遇到的可能是第四种类型,我们找到第四条,上面写着操作方法在第十二页。翻到第十二页,好,找到了。那么按照这个操作方法一步一步往下执行,就把这问题给解决了。然后呢,我们就可以继续刚才的运算了,对不对?可是我刚才是在哪一页进行的运算?这在哪儿呢?我怎么回到刚才的运算呢?
我们先来看一看在CPU内部是如何检测中断的。我们就以x86的实模式为例。在CPU内部,会有中断的处理电路。如果在运算时发生了异常的情况,比如出现除法错误,运算器就会产生相应的中断信号。如果遇到其他指令产生的中断,就会产生其他对应的控制信号。那这些中断都是和正在运行的程序本身有关的,所以我们叫它软件中断,或者是内部中断。
那还有来自CPU外部的中断。这些外设有可能是键盘、网络、打印机,等等。当键盘按键,或者有收发网络包的情况,这些外设的芯片或者板卡就会通过主板上的物理连线,发出中断请求信号。这些信号最终都会连接到CPU内部。不管来源是哪里,CPU检测到了中断请求信号,就会进行处理。
中断处理的过程是这样的。首先,CPU会关闭中断响应,也就是不再接受后面其他的外部中断请求了。注意,只是不接收外部中断请求。然后将发生中断处的这个指令的地址保存到栈中,也就是内存当中的一个区域,这个信息必须要保存好,以便于处理完中断后可以正确地返回当前的程序继续执行。那究竟是保存发生中断的这条指令的地址,还是发生中断的这条指令之后的一条指令的地址,这和这个中断具体的类型会有关系。第三步则由CPU识别中断的来源,确定中断类型号,从而能够找到相应的中断服务程序的入口地址。
这三步一般都是由硬件自动完成的。
第四步是保存现场,也就将中断服务程序中可能会改变的寄存器先压栈,也就是放到内存中保存起来。为什么要这么做呢?我们来看一个例子。
在这个主程序片段中,我们假设执行到这条除法指令的时候发生了中断。这时就会调用中断服务程序。我们假设在这中断服务程序当中,有 add cx,dx
这么一条指令,它会改写CX寄存器的值。
如果我们没有对CX寄存器进行保护的话,在中断返回之后,CX寄存器的内容就已经改变了。然后回到主程序继续往下执行时,这个CX寄存器的内容,就不再是主程序中之前传到CX寄存器中的内容了。这样就会导致主程序的错误。所以,在中断服务程序中,如果要用到CX寄存器,就应该先进行压栈,把当前CX寄存器的值保存到存储器中,然后在中断返回之前,再执行弹栈的操作。这样中断返回之后,CX寄存器的内容就没有变化,不会影响主程序的运行。
然后第五步,就是执行这个中断服务程序的主体内容。而且在中断服务程序中,可以在适当的时候重新开放中断,以便响应其他高优先级的外部中断。以免中断服务程序本身要执行比较长的时间,造成有高优先级的外部中断长时间无法得到响应。
那如何在中断服务程序当中,重新开放中断呢?
那这就涉及到对标志寄存器的操作。在标志寄存器当中有一些标志位是状态标志,状态标志一般情况下,是由硬件设置,然后由软件读取。例如进位标志,就是在运算器产生进位时,由硬件自动设置,然后由软件的指令读取出来,可能会进行相关的累加操作。而另一类标志称为控制标志,这些标志通常是由软件进行设置,然后硬件电路根据这些标志设置的不同,而执行不同的功能。
标志寄存器的第九位就是中断标志。
IF这个标志控制对可屏蔽中断的响应。
外部中断分为两大类,一类是可屏蔽中断,一类是非屏蔽中断。IF标志只对外部中断当中的可屏蔽中断起作用。如果IF等于1,那就允许CPU响应可屏蔽中断请求;如果IF等于0,那就不允许CPU响应可屏蔽中断请求。那怎么设置IF标志位的值呢?
有两条指令。STI指令就是把IF位置为1,而CLI指令就是把IF标志位清零。这两条指令都是没有操作数的指令。当然,IF指令对非屏蔽中断和内部中断都是不起作用的。
然后我们来看中断处理过程的最后一步,那就是恢复现场并且返回主程序继续运行。
返回主程序就需要执行中断返回指令。中断返回指令它可以不带操作数,那么IRET这条指令的操作,就是从当前的栈顶弹出三个字,分别送到IP、CS和FLAGS寄存器当中去。IRET这条指令是放在中断服务程序的末尾,在中断调用时,CPU中的硬件电路会将这三个寄存器的值压入栈中。所以,在执行中断返回指令时,也会由硬件从栈顶弹出这三个字,再放回到这三个寄存器当中,当CS和IP寄存器改变之后,下一条指令就会回到程序发生中断的地方继续执行了。
当然我们也注意到,IRET这条中断返回指令操作的是CS和IP寄存器。在32位和64位的x86中,指令指针寄存器,又被扩展为EIP和RIP寄存器,它们的宽度都是不一样的。所以,后来又有了不同的对应的指令。
我们再用一个图示来看一看刚才介绍的中断处理的过程。我们就以内部中断为例。
当CPU执行到某条指令,例如就是这条指令,如果此时发生中断,CPU内部就会产生中断信号,相关的中断处理电路会判断中断的来源,并产生中断类型号。CPU的硬件电路会将CS和IP寄存器压栈,这样就保存好了处理完中断后要返回的地址。同时硬件上还会将FLAGS寄存器压栈,以便保存好当前的各项标志,以免中断处理程序当中,有些指令会改变程序的标志位。在硬件上还会清除IF标志位,以起到关中断的作用。然后根据中断类型号,找到对应的中断向量,也就是新的CS和IP的值,并以此更新CPU当中的CS和IP寄存器。当完成这个操作后,CPU就会转到中断服务程序开始执行。那么在中断服务程序中,也可以执行STI指令,以开放中断。当完成了中断服务程序之后,最后一条就是执行中断返回指令(IRET)。这条指令会从存储器当中将刚才压栈的三个字弹出来,并按照对应的顺序存到CS、IP和FLAGS寄存器当中去,这样就完成了返回主程序的动作。
注:PSW即程序状态字(有些教材也叫程序状态寄存器),Program Status Word。
这就是中断处理过程的六个主要的步骤。通常情况下,前三步都是由处理中断的硬件电路来完成,后三部则是由软件,也就是中断服务程序来完成。
但这只是一个大体的分工,在真的要涉及中断服务程序的时候,必须要针对具体的系统,弄清楚软硬件的分工究竟是怎么样的。例如在保存现场这件事情上,刚才我们介绍的标志寄存器是由硬件来负责保存的,但是在另外的一些系统上,可能硬件就不会自动地保存标志寄存器,就需要由中断服务程序的软件来进行保存。
那么在设计中断服务程序时,必须要搞清楚这些问题,不然就有可能发生错误。
现在,我们不但有了这张表,而且有了一个规范的操作流程。我们知道运算中出现异常情况,怎么查到这张表,怎么找到异常的操作的步骤。然后,应该事先做哪些记录,以免丢失信息。最后我们还知道怎么回到我们刚才运算的那个地方,继续进行下一步的操作。
这就是一个完整的中断的处理过程。
原文地址:https://www.cnblogs.com/doctorx/p/9743277.html