9.6 中断任务和中断子程序(Interrupt Tasks and Interrupt Procedures)
和CALL指令能调用一个子程序或任务一样,中断、异常也可以“调用”一个子程序或任务来做中断处理程序。当识别了一个中断、异常时,处理器使用中断号来索引IDT。如果处理器索引到的是一个中断门或陷阱门,它就象CALL指令调用一个调用门一样调用一个中断处理子程。如果处理器索引到一个任务门,它就象CALL指令调用了一个任务一样,做任务切换。
9.6.1 中断子程序(Interrupt Procedures)
如图9-4所示,中断门、陷阱门间接地指向了一个在当前任务上下文里的子程序。门里的选择子指向了一个在GDT或LDT中的可执行代码段描述符。偏移部分字段则指向了中断、异常处理子程序的入口。
80386象使用CALL指令一样唤醒一个中断、异常处理子程序。不同之处在下面介绍。
9.6.1.1 中断子程堆栈(Stack of Interrupt Procedure)
和CALL指令的控制转移一样,当控制转移到中断、异常处理子程序时,程序使用堆栈来存储返回被中断程序的一些信息。如图9-5所示,中断时,先把EFLAGS寄存器推入堆栈,然后再是返回地址。
某些类型的异常还可以引起一个出错码,出错码被压入堆栈。异常处理程序可以使用出错码来帮助排错。
9.6.1.2 从中断子程序返回(Returning from an Interrrupt Procedure)
中断处理程序的返回方式也不和一般的子程序相同。IRET指令用来从中断子程序中返回。IRET和RET指令相似,只是要增加EIP额外的4个字节(因为在堆栈上的标志)和把保存的标志。只有当CPL为0的时候,标志寄存器的IOPL字段才可以改变。IF标志位只有当CPL<=IOPL时,才会改变。
9.6.1.3 被中断子程使用的标志位(Flags Usage by Interrupt Procedure)
不管是中断门还是陷阱门,中断发生,且当前的TF被保存在堆栈上时都会清除TF(陷阱标志)标志位。这样,处理器就可以在中断处理程序中禁止用于调试的单步中断异常。下一个IRET指令将从堆栈上的EFLAGS寄存器映象恢复TF位。
中断门和陷阱门的主要区别是对于IF(允许中断标志)标志位的影响。通过中断门进入中断处理程序后会清除IF位,从而禁止了其它中断的发生。其后的IRET指令会恢复IF位到堆栈上的EFLAGS映象。通过陷阱门进入的中断不改变IF位。
9.6.1.4 在中断子程序内的保护(Protection in Interrupt Procedures)
对于中断子程序的特权级规则和普通的子过程调用类似:CPU不允许中断控制从当前特权级到低特权级。一个试图破坏这个规则的操作将引起通用保护异常。
因为中断的发生一般是可预测的,这种特权级的约束着中断、异常处理程序的执行。以下的方法都可以用来防止这个规则的破坏。
把处理程序放到一个一致性段中。这种策略可以处理一些异常(比如,除法错)。这样的处理程序只能使用堆栈上的数据。如果它需要在数据段时的数据,数据段应该为特权级3,让它不被任何保护。
把处理程序放在特权级0的段。
9.6.2 中断任务(Interrupt Tasks)
在IDT中的任务门间接的指向了一个任务,如图9-6所示。门里的选择子字段指向了一个GDT中的TSS描述符。
当一个IDT中的中断、异常向量指向一个任务时,任务切换发生。将中断用任务来处理有以下两个好处:
上下文被完整的自动保存。
中断处理程序可以通过一个完全隔开的地址空间,和其它任务完全隔开,通过了LDT和页目录。
处理器的任务切换操作在第7章中已讲述。中断任务通过执行一条IRET指令返回到被中断的任务。
如果任务切换是被一个带出错码的异常引起的话,处理器将自动压入出错码到中断任务的第一条指令特权级的对应的堆栈中。
当在80386中的操作系统中使用中断任务时,实现上就有两个调度器:一个软件调度器(操作系统的一部分)和一个硬件调度器(处理器中断机制的一部分)。软件调度器设计时应该考虑到,只要中断允许时,硬件调度器可能在任意时间指派中断任务。