CPU支持256个中断向量,早期的CPU把内存中从0开始的1K字节作为一个中断向量表,当从引脚中得到中断信号后,便读取中断向量号,并根据中断向量号得到相应的中断服务程序的入口地址,从而执行入口地址。
CPU进入保护模式后,对中断响应做了大幅度修改。首先中断向量表的表项从单纯的入口地址改变了意义,称之为“门”。意思为中断发生后,只有通过这些门才可以进入中断服务程序。但门的作用不仅仅限于中断,只要想切换CPU的运行状态就得通过门。因此门根据其用途被分为4种:任务门,中断门,陷阱门和调用门。除任务门外,其它们的结构类似。门的大小都是8字节,64位。
1。任务门
从高向低位看,前16位闲置不用,第17位为P标志位(1表示在内存,0表示不在内存)。第18位至第20位是描述项的优先级别。一个0位,第22位到25位是类型码为101标识任务门。第26位至33位闲置不用。第34位到50位为TSS段选择码,最后16位闲置不用。
P标志位的意义,表示所指向的中断服务程序是否在内存。
描述项的优先级别,用于权限控制。(后面会详细介绍)
当CPU相应中断后,TSS段选择码会起到相当于寄存器CS和DS的作用。TSS是16位,高13位便是内存页面表项,类似于CS和DS,根据第14位选择GDT或者LDT。这样就指向一个特殊的地方。称为“任务状态段”TSS(貌似没经过页式内存管理),其中TSS保存了CPU运行现场包括各个寄存器信息。CPU接下来会将TSS中的运行现场载入到CPU(在次之前CPU会把载入前的现场保存到一个相应的TSS中,CPU中新增的寄存器TR会指向这个TSS)。这样就完成了一次任务切换。实际上linux不采用这种门来实现任务的切换,对于操作系统来说任务也就是进程(可以这么说吧)。但进程带有的信息远远大于CPU中的任务。
2.另外3种门的结构类似。从高向低看,最高16位为位移高16位。接下来是P位,DPL位,一个D位(D位1表示32位,0表示16位)和类型码位(110中断门,111陷阱门,100调用门),3个0占了3个位置,5个闲置不用位,16个段选择码(TSS不知道能不能这么叫,书上没这么写,还是不叫TSS了),和位移的低16位。由此看来类型码和上面的任务门处于同一位置,这样大概硬件不管碰到哪种门都可以取得正确的类型码,然后决定如何来解析这个数据结构吧。和上面区别是,上面的任务门不是一个程序的入口,而是一个任务入口,也就是进程入口(当然linux可能不承认,但CPU设计人员可不这么想)。程序的新入口是需要段内位移的(任务当然不需要)。中断门和陷进门的区别是在于中断门会关闭中断标志位(也就是将CPU中一个用于控制是否该相应中的寄存器赋值为0,这样就屏蔽了可屏蔽中断,但像断电这种不能屏蔽中断自然屏蔽不了),从而防止嵌套中断的出现。而陷阱门则不会(为什么这么分,这和中断的类型有关)。
16位的段选择码和TSS意思差不多,跟CS和DS寄存器的意义类似,高13位为段表的下标,根据第14位选择LDT或者GDT,找到段描述结构(保护模式段式内存寻址的内容),这样我们就获得了一个TSS(已经不新鲜了,内存地址印射不知道看了多少遍了)。
DPL的意义,硬件上实现非常复杂。用简单的目光看下(所谓的简单目光就是不看任务门和调用门的实现过程),当一个中断到的时候,我们根据中断项找到一个中断门(也可能是陷阱门),这是硬件就会拿CPU的CPL(CPU的运行级别,也就是段寄存器的最低3位了)和DPL对比。CPL必须小于或者等于DPL,就穿过了这道门。我们就会到了新程序的入口了,再将它的CPL和DPL相比,DPL应该小于等于CPL,这样就有一个不等式,中断前的CPL=
门介绍完了,现在看看中断的流程:首先CPU接收到中断信号,响应中断(如果它能够响应这个中断的话)。CPU会将当前的EFLAGS寄存器内容压入堆栈(至于EFLAGS寄存器干嘛用的,读者可以自己看看CPU的逻辑图查查,我只隐约记得貌似是用来保存一些控制信息的,比如是否响应中断的寄存器就是其中一个位置,知道的回帖说下),返回地址也会被压入堆栈(返回地址就是CS和取令指针EIP的内容了)。如果中断是异常引起的(比如除以0异常),则表示异常原因的出错代码也会被压入栈中。进一步,如果运行级别前后也发生了变化,那堆栈也会被更换(堆栈更换的原因:运行级别不同了,堆栈空间自然要更换,你不应该在系统状态下使用用户堆栈空间,或许这没什么,但如果是在用户状态下使用系统堆栈呢?至于新的堆栈,也就是新的SS和ESP寄存器的值则是在TSS这个数据结构中,TSS寄存器中本来包含一个ES和ESP,这是这个任务的堆栈,还包含3个额外的ES和ESP,分别用于0,1,2这3个不同级别下的堆栈,为什么这么做,我也不太清楚,或许看到后面能明白吧)。CPU就根据TR寄存器内容找到TSS,用相应的堆栈来替换原来的,这样原来的堆栈指针也要被压入新的堆栈中。(有点复杂)。
最后,中断向量表已经不限制于内存0地址这个位置了,它的起始地址保存在新增寄存器IDTR中。
阅读(978) | 评论(0) | 转发(0) |