一个操作系统的实现(10)-中断和异常的补充说明
2016-06-10 12:29
369 查看
这节对中断和异常机制做一些额外的补充说明。还介绍了保护模式下的I/O操作。最后总结了一下前面学习的保护模式。
上节的代码始终运行在ring0层,没有涉及特权级的变换。这里需要说明的是,中断和陷阱过程的特权级变换规则与调用门涉及的特权级变换规则是完全相同的。
无特权级变换的中断发生时的堆栈变化和有特权级变换的中断发生时的堆栈变化如下:
![](http://7qn8kg.com1.z0.glb.clouddn.com/OS-stack-change-interrupt.png)
如果中断或异常发生时没有特权级变换,那么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的控制权限是很重要的一项内容,保护模式通过IOPL和I/O许可位图来对保护模式下的I/O进行控制。
IOPL位于寄存器eflags的第12、13位,如下图所示:
![](http://7qn8kg.com1.z0.glb.clouddn.com/OS-eflags.png)
指令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将维持原值,不会产生任何异常。
回过头来看看TSS的结构,如下:
![](http://7qn8kg.com1.z0.glb.clouddn.com/OS-TSS.png)
会发现在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等内容进行非常严格的检验,同时可能伴随堆栈的切换,这都对不同层级的程序进行了保护。
特权级变换规则
上节的代码始终运行在ring0层,没有涉及特权级的变换。这里需要说明的是,中断和陷阱过程的特权级变换规则与调用门涉及的特权级变换规则是完全相同的。
中断或异常发生时的堆栈变化
无特权级变换的中断发生时的堆栈变化和有特权级变换的中断发生时的堆栈变化如下:![](http://7qn8kg.com1.z0.glb.clouddn.com/OS-stack-change-interrupt.png)
如果中断或异常发生时没有特权级变换,那么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位,如下图所示:![](http://7qn8kg.com1.z0.glb.clouddn.com/OS-eflags.png)
指令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的结构,如下:![](http://7qn8kg.com1.z0.glb.clouddn.com/OS-TSS.png)
会发现在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等内容进行非常严格的检验,同时可能伴随堆栈的切换,这都对不同层级的程序进行了保护。
相关文章推荐
- Ruby中的异常处理代码编写示例
- SQL Server 2005 中使用 Try Catch 处理异常
- MySQL抛出Incorrect string value异常分析
- Mysql IO 内存方面的优化
- 浅谈C#中简单的异常引发与处理操作
- 详解C#编程中异常的创建和引发以及异常处理
- Node.js 的异步 IO 性能探讨
- 详解JavaScript中的异常处理方法
- java程序中的延时加载异常及解决方案
- 解析Java异常的栈轨迹及其相关方法
- .NET(C#):Emit创建异常处理的方法
- windows7服务器上weblogic启动失败异常解决方法
- SQL Server误区30日谈 第22天 资源调控器可以调控IO
- mysql 数据同步 出现Slave_IO_Running:No问题的解决方法小结
- java中的Io(input与output)操作总结(一)
- 有关ajax的error与后台的异常问题解决
- 深入探讨JAVA中的异常与错误处理
- GO语言异常处理机制panic和recover分析
- GO语言的IO方法实例小结
- java中的Io(input与output)操作总结(四)