上一次转的文章(为什么保护模式跳回实模式要选择一个NormalSelectorhttp://blog.csdn.net/ruyanhai/article/details/7186970)中提到了描述符的高速缓存,但那篇文章说的不是很清楚,因此这里又总结了一下。
blog.csdn.net/ruyanhai
众所周知,保护模式下,GDT存储在内存中,为了加快访问GDT中相关描述符的速度,x86系列CPU为每个段选择器设置了对应的缓存寄存器。每个段选择器(CS, SS, DS, ES, FS, GS)的缓存寄存器,存储着该选择器对应的GDT描述符的内容,因此也拥有着与描述符同样的结构。
当我们修改段选择器时,比如MOV DS, AX(显示的修改DS),或者JMP 0:0x1234(隐式的修改CS),都会用GDT中相应描述符刷新缓存。
而在实模式下,虽然我们不使用GDT,但这些缓存寄存器仍然是存在并且有效的,只是处理器会产生一些默认值放在这些高速缓存中,而在实模式下,我们是没法改变这些值的。所以当我们从保护模式跳回实模式时,如果没有重新加载段选择器,这些高速缓存中的部分字段会从保护模式继承下来,而不会被更新的,比如段界限字段(segment limit),保护模式使用20位段界限,而实模式只有16位。
存储在内存里的GDT被更新的时候,相应的高速缓存不会被更新,因此此时必须重新加载段寄存器(段选择器),以保证段选择器高速缓存中缓存最新的描述符。这种独立性可以保证删除GDT的时候不会破坏系统,或者说可以防止高速缓存中载入错误的描述符。但此时必须关闭中断,因为中断或异常会隐式的重新加载CS。
根据以上的知识,也解释了为什么不能从32位代码段跳回实模式,是为了正确的设置CS选择器对应的高速缓存。因为我们无法显示的修改CS段寄存器,而是必须通过一个JMP。我们设置一个16位代码段的描述符,当我们从32位代码段跳入16位代码段,CS对应的高速缓存中的各种字段也就被设置成该描述符(实模式)的值,此时方可安全转入实模式。
总结一下就是:
- 为了加速GDT的访问,x86系列CPU的每个段寄存器对应的描述符都有一个高速缓存。
- 当我们写入段寄存器时,相应的高速缓存也被更新(更新为修改后段寄存器对应的GDT描述符)
- 实模式下,这些高速缓存是存在并且有效的,但不能被修改
- 保护模式跳回实模式,高速缓存中的部分字段会被继承,而不被更新(比如段界限),因此必须在跳回前加载一个normal selector
- 直接修改内存中的GDT,不会导致高速缓存更新,因此必须重新加载段寄存器。