0%

异常和中断处理

内部异常:

指由处理器内部异常引起的意外事件。

根据其发生的原因又分为硬故障中断和程序性异常。

根据异常情况的性质和严重性, 异常又分为以下三种:

故障(失效):它是在因其故障的指令启动后、执行结束前被检测到的一类异常事件。

例如、指令译码时、出现“非法操作码”;取指令或数据时,发生“段不存在”、“缺页”或“保护错”;执行整数除法指令时,发现“除数为0”等。显然,“段不存在”、“缺页”等这类异常处理后,已将需要的段或页面从磁盘侧到主存所以可继续回到发生故障的指令维续执行,此时断点为当前发生故障的指令:对于”非法操作码”、“保护错”“除数为0”等,因为无法通过异常处理程序恢复故障,因此不能回到原断点继续执行,必须终止进程的执行;对于浮点除法“除数为0”,有不同处理方式,可将指令执行结果用特殊的值(如∞或NaN)来表示后继续回原断点执行,也可终止进程的执行。

陷阱(自陷):与故障等其他意外发生的异常事件不同,是预先安排的一种“异常”事件,就像预先设定的“陷阱”一样,首先通过某种方式将CPU设定为处于某个特定状态,在程序执行过程中,一旦某条指令的执行发生了相应状态所满足的条件,则CPU调出特定的程序进行相应的处理。

通常的做法是:事先在程序中用一条特殊指令或通过某种方式设定特殊控制标志来人为设置一个“陷阱”,当执行到被设置了“陷阱”的指令时,CPU在执行完自陷指令后,自动根据不同“陷阱”类型进行相应的处理,然后返回到自陷指令的下一条指令执行。注意,当自陷指令是转移指令时,并不能返回到下一条指令执行,而是返回到转移目标指令执行。

终止:如果在执行指令过程中发生了使机器无法继续执行的硬件故障,如电源掉电、线路故障等,则程序将无法继续执行,只好终止,此时,调出中断服务程序来重启系统。这种异常与故障和自陷不同,不是由特定指令产生的,而是随机发生的。

外部中断

程序执行过程中,若外设完成任务或发生某些特殊事件(如打印机缺纸、定时采样计数时间到、键盘缓冲满等),会向CPU发中断(Interrupt)请求,要求CPU对这些情况进行处理。通常,每条指令执行先后,CPU都会主动去查询有没有中断请求,有的话,则将下条令地址作为断点保存,然后转到相应的中断服务程序执行:结束后回到断点继续执行。

这种事件与执行的指令无关,由CPU外部的I/O发出,所以,称为I/O中断或外部中断请求线向CPU请求。

异常处理过程

在CPU执行指令过程中,如果发生了异常事件,则CPU必须进行相应的处理。CPU对异常的处理过程可分为以下两个步骤:保护断点和程序状态,识别异常事件并转异常处理。

1.保护断点和程序状态

前面提到,对于不同的异常事件,其返回地址(即断点)不同。例如.故障的断点是发生故障的当前指令的地址,自陷的断点则是自陷指令后面一条指令的地址。显然,断点的值由异常类型和发生异常时PC的值决定。例如,对于多周期数据通路,如果在执行到发生缺页的指令重新执行一遍,因而其断点值应该是当前PC值减4。因为,在发现“缺页”时,已在取指令状态执行了PC+4,所以,PC必须减4才能保证“缺页”异常处理返回后重新执行lw/sw指令。

为了能在异常处理后正确返回到原被中断程序继续执行,数据通路必须能正确计算断点值。假定计算出的断点值存放在PC中,则保护断点时,只要将PC送到栈或特定的寄存器中即可。

为了能够支持异常或中断的嵌套处理,大多数处理器将断点保存在栈中,如80x86处斗器的断点被保存在栈中。如果系统不支持多重中断嵌套处理,则可以将断点保存在特定守存器中,而不需送到栈保存,如MIPS处理器用EPC寄存器专门存放断点。

因为异常处理后可能还要回到原被中断的程序继续执行,所以,被中断时原程序的状态(如产生的各种标志信息、允许自陷标志等)都必须保存起来。通常每个正在运行程序的状态信息存放在一个专门的寄存器中,这个专门寄存器统称为程序状态字寄存器(PSWR),存放在PSWR中的信息称为程序状态字(Program Status Word,Psw).例如,在80x86中,程序状态字寄存器就是标志寄存器EFLAGS.与返回地址(即断点)样,PSW也要被保存到栈或特定寄存器中,在异常返回时,将保存的PSW恢复到PSWR中。

2.识别异常事件并转异常处理

在调出异常处理程序之前.必须知道发生了什么异常。一般来说,内部异常事件和外部中断源的识别方式不同,大多数处理器会将两者分开来处理。内部异常事件的识别大多采用软件识别方式,而外部中断源则可以采用软件识别或硬件识别方式。

软件识别方式是指,CPU设置一个异常状态寄存器,用于记录异常原因。操作系统使用一个统一的异常查询程序,该程序按-定的优先级顺序查询异常状态寄存器,先查询到的异常先被处理。例如,MIPS就采用软件识别方式,CPU中有一个cause寄存器,位于地址0x8000 0180处有一个专门的异常查询程序,它通过查询cause寄存器来检测异常类型,然后转到内核中相应的异常处理程序进行具体的处理。

因为像故障和陷阱之类的内部异常通常是在执行某条指令时发现的,可以通过对指令执行过程中某些条件的判断来发现是否发生了异常,而且一旦发现可以马上进行处理,所以,内部异常事件也可以不通过专门的查询程序来识别,而在发现异常时直接得到异常错误代码,根据不同的错误代码,转到相应的异常处理程序即可。80x86的处理方式就是这样。由于外部中断的发生与CPU正在执行的指令没有必然联系,相对于指令来说,外部中断是随机的、与当前执行指令无关的。所以,并不能根据指令执行过程中的某些现象来判断是否发生了中断请求。因此,对于外部中断,只能在每条指令执行完后、取下条指令之前去查询是否有单断请求。想常CPU通过采样对应的中断请求引脚线来进行套询:如果发现0x800 0180处有一个专门的异常 查询程序,它通过查询cause寄存器来检测异常类型,然后转到内核中相应的异常处理程序进行具体的处理。

因为像故障和陷阱之类的内部异常通常是在执行某条指令时发现的,可以通过对指令执行过程中某些条件的判断来发现是否发生了异常,而且一旦发现可以马上进行处理,所以,内部异常事件也可以不通过专门的查询程序来识别,而在发现异常时直接得到异常错误代码,根据不同的错误代码,转到相应的异常处理程序即可。80x86的处理方式就是这样。由于外部中断的发生与CPU正在执行的指令没有必然联系,相对于指令来说,外部中断是随机的、与当前执行指令无关的。所以,并不能根据指令执行过程中的某些现象来判断是否发生了中断请求。因此,对于外部中断,只能在每条指令执行完后、取下条指令之前去查询是否有中断请求。通常CPU通过采样对应的中断请求引脚线来进行查询,如果发现“中断请求”线有效,则说明有中断请求,但是到底是哪个中断源发出的请求,还需要进一步识别。

假定在执行异常处理程序时又发生了新的异常,怎么办?在发现异常并转到异常处理程序的过程中,若在保存正在运行程序的现场时又发生新的异常,那么,就会因为要处理新的异常,而把原来进程的现场、返回的断点和程序状态等破坏掉,因此,应该有种机制来禁止在响应并处理异常时再响应新的异常。通常通过设置“中断/异常允许”状态位(或“中断/异常允许”触发器)来实现。当“中断/异常允许”状态位置1,则为“开中断”状态,表示允许响应中断/异常;若“中断/异常允许”状态位被清零,则为“关中断”状态,表示不允许响应中断/异常。一-般由操作系统通过管态指令来设置该位的状态。例如,在80x86体系结构中,可以执行指令STI或CLI,使标志寄存器EFLAGS中的IF位被设置为1或清零,使CPU处在”开中断“和“关中断”