IA-32体系结构CPU保护模式常见误区

来源:互联网 发布:地下城与勇士台服源码 编辑:程序博客网 时间:2024/05/17 05:52
 

系统软件开发系列文章之一:IA-32体系结构CPU保护模式常见误区

 

(20100603随笔版,不保证完全的学术严谨)

 

立志于成为系统程序员的同学们,或者尝试操作系统开发的程序员们,在阅读IA-32体系结构(以下简称80386)保护模式相关资料时,常常被一些表述不清晰,甚至可能存在理解错误的资料所误导。笔者简单总结了80386保护模式中的一些常见误区,这些误区曾经被国内的一些教材反复引用,在此列出,以供学习者参考。

 

1、80386保护模式下的段寄存器是16位的

 

错误!实际上无论实模式还是保护模式,80386的段寄存器都是80位,但只有16位是可见的,也就是说,程序员只能访问80位段寄存器中的16位。

 

段寄存器中64位的不可见部分,一般资料称之为“描述符高速缓存寄存器”或者“影子寄存器”,这一部分是真正控制80386 CPU内部硬件电路寻址的寄存器,其内容就是一个描述符。

 

保护模式下,当修改段寄存器中16位的可见部分(也就是程序员访问的CS、DS、SS、ES、FS和GS)时,80386认为这16位可见部分是一个段选择子(Selector),于是根据选择子中的索引,在GDT或者LDT中定位对应的描述符,检查保护模式段保护规则(特权级)没有被违反后,将对应的描述符装载到段寄存器不可见部分中,80386 CPU内部硬件电路即根据段寄存器不可见部分中的描述符对应的基址和限长进行线性地址寻址。

 

实模式下,段寄存器64位不可见部分的内容仍然是一个描述符(自动生成的16位段描述符),对应的限长是64KB,基址是段寄存器16位可见部分左移4位,当16位可见部分发生改变时,自动生成新的基址,并自动更新64位不可见部分中的描述符。80386 CPU内部硬件电路还是根据段寄存器不可见部分中的描述符对应的基址和限长进行寻址。

 

可以看出,无论是实模式还是保护模式,实际寻址都是由段寄存器64位不可见部分决定的,实模式和保护模式的区别只在于生成段寄存器64位不可见部分中的描述符的方式不同。

 

需要特别注意的一点是:如果不改变段寄存器16位可见部分,也就是程序员不修改CS、DS、SS、ES、FS或者GS,那么64位不可见部分的内容就不会被重新装载或者更新,即使发生了实模式和保护模式之间的切换也是一样。

 

2、修改CR0寄存器中的PE位之后需要执行一条远跳转指令才能进入保护模式

 

错误!只要将CR0寄存器中的PE位设置为1,80386就已经进入了保护模式,但是如果没有修改CS的内容,则对应的64位不可见部分中的内容还是实模式下自动生成的描述符,并没有自动从GDT或者LDT中装载新的描述符,内存指令寻址方式并没有发生变化。这一点是必需的,否则如果80386一旦进入保护模式就自动装载新的描述符,则内存指令寻址方式立即发生变化,对紧随进入保护模式指令之后的后续指令,就无法正确从内存中取指令并执行。

 

进入保护模式之后的远跳转指令的作用,实际上就是更新CS的内容,由于此时已经进入保护模式,才会自动从GDT或者LDT中装载新的描述符到CS对应的64位不可见部分中,这样描述符指定的保护模式分段寻址方式才被真正启用。

 

换句话说,将CR0寄存器中的PE位设置为1可以让80386进入保护模式,但只有通过远跳转指令等方法修改了CS的内容,才能启用GDT或者LDT中描述符指定的保护模式分段寻址方式。

 

3、实模式下一定不能访问4GB内存

 

错误!先进入保护模式,在保护模式下,选择一个实模式操作系统(例如DOS)下通常不被使用的附加数据段寄存器(例如FS),修改其内容(16位可见部分内容),使其定位到GDT或者LDT中一个基址为0,限长为4GB的数据段描述符,这样描述符被自动装载到FS对应的64位不可见部分中,随后退回到实模式,只要不更新FS的内容,64位不可见部分中的内容仍然是基址为0,限长为4GB的数据段描述符,这样通过FS和对应32位偏移量就能访问到4GB内存。

 

早年讲述DOS高级编程的一些书籍上都介绍过“实模式下访问4GB内存”的方法,其方法原理正是如此。

 

4、段保护的实质是什么?

 

在保护模式分段寻址方式下,真正控制80386 CPU硬件电路寻址的是段寄存器64位不可见部分中的描述符,因此只要阻止违反段保护规则的描述符被装载到段寄存器64位不可见部分中,即可实现段保护。80386 CPU是不可能在从内存中取每一条指令,或者读写每一个字节数据时都进行特权级检查的,这样会严重降低CPU的效率。

 

这也是为什么段寄存器有64位不可见部分的原因,因为这一部分只能在不违反段保护规则的情况下更新,不能由程序员自由修改,因此对程序员不可见。

 

除某些特殊情况(例如通过调用门进行的调用)之外,段保护的一般规则是:

 

CPL:当前特权级

RPL:请求特权级(准备更新段寄存器16位可见部分的选择子确定的特权级)

DPL:描述符特权级(准备装载到段寄存器64位不可见部分中的描述符确定的特权级)

 

则要求CPL和RPL都不低于DPL(如果进行特权级数字的比较,应该是不大于),即(CPL<=DPL)&&(RPL<=DPL)。

 

 http://blog.csdn.net/changjiang/article/details/5645071

原创粉丝点击