1 《Undocumented Windows 2000 Secrets》翻译 --- 第四章( 六 )





补充:

这部分内容选择自《 Windows 环境下 32 位汇编语言程序设计》

x86 的内存分页机制
当 x86 CPU 工作在保护模式和虚拟 8086 模式时,可以使用全部 32 根地址线访问 4GB 的内存 。因为 80386 的所有通用寄存器都是 32 位的,所以用任何一个通用寄存器来间接寻址,不必分段就可以访问到 4GB 的内存地址 。
但 这并不意味着,此时段寄存器就不再有用了 。实际上,段寄存器更加有用了,虽然在寻址上没有分段的限制了,但在保护模式下,一个地址空间是否可以被写入,可 以被多少优先级的代码写入,是不是允许执行等等涉及保护的问题就出来了 。要解决这些问题,必须对一个地址空间定义一些安全上的属性 。段寄存器这时就派上了 用场 。但是设计属性和保护模式下段的其他参数,要表示的信息太多了,要用 64 位长的数据才能表示 。我们把这 64 位的属性数据叫做段描述符( Segment Descriptor ) 。
80386 的段寄存器是 16 位的,无法放下保护模式下 64 位的段描述符 。如何解决这个问题呢?方法是把所有段的段描述符顺序存放在内存中的指定位置,组成一个段描述符表( Descriptor Table );而段寄存器中的 16 位用来做索引信息,指定这个段的属性用段描述符表中的第几个描述符来表示 。这时,段寄存器中的信息不再是段地址了,而是段选择器( Segment Selector ) 。可以通过它在段描述符表中“选择”一个项目已得到段的全部信息 。
那么段描述符表存放在哪里呢? 80386 引入了两个新的寄存器来管理段描述符表 。一个是 48 位的全局描述符表寄存器 GDTR ,一个是 16 位的局部描述符表寄存器 LDTR。那么,为什么有两个描述符表寄存器呢?
GDTR 指向的描述符表为全局描述符表 GDT ( Global Descriptor Table ) 。它包含系统中所有任务都可用的段描述符,通常包含描述操作系统所使用的代码段、数据段和堆栈段的描述符及各任务的 LDT 段等 。全局描述符表只有一个 。
LDTR 指向局部描述符表 LDT ( Local Descriptor Table ) 。80386 处理器设计成每个任务都有一个独立的 LDT。它包含每个任务私有的代码段、数据段和堆栈段的描述符,也包含该任务所使用的一些门描述符,如任务门和调用门描述符等 。
不同任务的局部描述符分别组成不同的内存段,描述这些内存段的描述符当作系统描述符放在全局描述符表中 。和 GDTR 直接指向内存地址不同, LDTR 和 CS 、 DS 等段选择器一样只存放索引值,指向局部描述符内存段对应的描述符在全局描述符表中的位置 。随着任务的切换,只要改变 LDTR 的值,系统当前的局部描述符表 LDT 也随之切换,这样便于个任务之间数据的隔离 。但 GDT 并不随着任务的切换而切换 。
16 位的段选择器如何使用全局描述符表和局部描述符表这两个表呢?实际上,段选择器中只有高 13 位表示索引值 。剩下的 3 个数据位中,第 0 , 1 位表示程序的当前优先级 RPL ;第 2 位 TI 位用来表示在段描述符的位置; TI=0 表示在 GDT 中, TI=1 表示在 LDT 中 。
80386 处理器把 4KB 大小的一块内存当作一“页”内存,每页物理内存可以根据“页目录”和“页表”,随意映射到不同的线性地址上 。这样,就可以将物理地址不连续的内存的映射连到一起,在线性地址上视为连续 。在 80386 处理器中,除了与 CR3 (保存当前页目录的地址)相关的指令使用的是物理地址外,其他所有指令都是使用线性地址寻址的 。
是否启用内存分页机制是由 80386 处理器新增的 CR0 寄存器中的位 31 ( PG 位)决定的 。如果 PG=0 ,则分页机制不启用,这时所有指令寻址的地址(线性地址)就是系统中实际的物理地址;当 PG=1 的时候, 80386 处理器进入内存分页管理模式,所有的线性地址要经过页表的映射才得到最后的物理地址 。

推荐阅读