您的位置:首页 > 其它

一个操作系统的实现(10)-中断和异常的补充说明

2016-06-10 12:29 369 查看
这节对中断和异常机制做一些额外的补充说明。还介绍了保护模式下的I/O操作。最后总结了一下前面学习的保护模式。


特权级变换规则

上节的代码始终运行在ring0层,没有涉及特权级的变换。这里需要说明的是,中断和陷阱过程的特权级变换规则与调用门涉及的特权级变换规则是完全相同的。


中断或异常发生时的堆栈变化

无特权级变换的中断发生时的堆栈变化和有特权级变换的中断发生时的堆栈变化如下:





如果中断或异常发生时没有特权级变换,那么eflags、cs、eip将依次被压入堆栈,如果有出错码的话,出错码将在最后被压栈。有特权级变换的情况下同样会发生堆栈切换,此时,ss和esp将被压入内层堆栈,然后是eflags、cs、eip、出错码(如果有的话)。

从中断或异常返回时必须使用指令iretd,它与ret很相似,只是它同时会改变eflags的值。需要注意的是,只有当CPL为0时,eflags中的IOPL域才会改变,而且只有当CPL≤IOPL时,IF才会被改变。

另外,iretd执行时Error Code不会被自动从堆栈中弹出,所以,执行它之前要先将它从栈中清除掉。


中断门和陷阱门的区别

实际上,中断门和陷阱门存在一个很微小的差别,就是对中断允许标志位IF的影响。由中断门向量引起的中断会复位IF,这样可以避免其他中断干扰当前中断的处理。随后iret指令会从堆栈上恢复IF的值;而通过陷阱门产生的中断不会改变IF。


保护模式下的I/O

毫无疑问,对I/O的控制权限是很重要的一项内容,保护模式通过IOPL和I/O许可位图来对保护模式下的I/O进行控制。


IOPL

IOPL位于寄存器eflags的第12、13位,如下图所示:





指令in、ins、out、outs、cli、sti只有在CPLIOPL时才能执行。

这些指令被称为I/O敏感指令(I/O Sensitive Instructions)。如果低特权级的指令试图访问这些I/O敏感指令将会导致常规保护错误(#GP)。

可以改变IOPL的指令只有popf和iretd,但只有运行在ring0的程序才能将其改变。运行在低特权级下的程序无法改变IOPL,不过,如果试图那样做的话并不会产生任何异常,只是IOPL不会改变,仍然保持原样。

指令popf同样可以用来改变IF(就好像执行了cli和sti)。然而,在这种情况下,popf也变成了I/O敏感指令。只有CPL≤IOPL时,popf才可以成功将IF改变,否则IF将维持原值,不会产生任何异常。


I/O许可位图

回过头来看看TSS的结构,如下:





会发现在TSS偏移102字节处有一个被称做“I/O位图基址”的东西,它是一个以TSS的地址为基址的偏移,指向的便是I/O许可位图。之所以叫做位图,是因为它的每一位表示一个字节的端口地址是否可用。如果某一位为0,则表示此位对应的端口号可用,为1则不可用。由于每一个任务都可以有单独的TSS,所以每一个任务可以有它单独的I/O许可位图。

比如,有一个任务的TSS是这样的:

由于I/O许可位图开始有12字节内容为0FFh,即有12×8=96位被置为1,所以从端口00h到5Fh共96个端口地址对此任务不可用。同理,接下来的1字节只有第1位(从0开始数)是0,表示这一位对应的端口(61h)可用。

I/O许可位图必须以0FFh结尾。在下面的代码中我们就是这样做的。


如果I/O位图基址大于或等于TSS段界限,就表示没有I/O许可位图,如果CPL≤IOPL,则所有I/O指令都会引起异常。I/O许可位图的使用使得即便在同一特权级下不同的任务也可以有不同的I/O访问权限。


保护模式小结

前面讨论的保护模式的含义如下:
在GDT、LDT以及IDT中,每一个描述符都有自己的界限和属性等内容,是对描述符所描述对象的一种限定和保护。
分页机制中的PDE和PTE都含有R/W以及U/S位,提供了页级保护。
页式存储的使用使应用程序使用的是线性地址空间而不是物理地址,于是物理内存就被保护起来。
中断不再像实模式下一样使用,也提供特权检验等内容。
I/O指令不再随便使用,于是端口被保护起来。
在程序运行过程中,如果遇到不同特权级间的访问等情况,会对CPL、RPL、DPL、IOPL等内容进行非常严格的检验,同时可能伴随堆栈的切换,这都对不同层级的程序进行了保护。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息