按照32位X86的MMU规范,采用多级页表机制,页表通过页目录所引,因为页目录本身也是一个页表,它是页表的页表。页目录常驻物理内存在进程初始化时分配,大小一个页面,由于它也是一个页表,而页表由页目录索引,那么页目录这个页面本身也应该有一个指向它自身的映射项!
假设该页目录项在虚拟地址空间的地址为B1|B2|B3,其中B1是高10位,代表页目录项的索引,B2为中间10位,为页表项索引,B3为页内偏移。实际上,我们可以看到,由于页目录是页表的页表,也是一个页表,所以高10位索引的页目录项指向该页目录本身,由于现在要定位的物理页面就是页目录,因此中间10位索引指向的页面也是页目录本身,一个进程的页表虚拟地址和整个地址空间是线形映射的,不能有两个表项指向同一个页面,因此B1和B2是相同的!最后,B3指向该目录项在本页的偏移索引!如下图所示:
通过以上的分析和图示,我们可以明显看出,B1,B2,B3的关系,即B1=B2=B3,只要满足这个,那就是所谓的自映射!这就明确导出了自映射机制,原来它只是Windows如此组织虚拟地址空间的一个结果罢了!
在讨论自映射的时候,并没有谈到页表虚拟地址和地址空间虚拟地址线性映射的影响,实际上有了这个线性映射的前提,我们可以根据自映射页目录项的虚拟地址B1|B2|B3导出所有页表的起始虚拟地址,这个的关键就是B3,虽然B1,B2和B3都是相等的。B3是页内偏移,也就是说第B1个页目录项处在页目录物理页面的第B3项这个位置,而这个位置就是它本身!换句话说,这个页目录是第B3个页表(永远记住,页目录是一个页表)!那么就是说在它前面有B3-1个页表,由于页表的虚拟地址是连续的,那么就可以推算出页表开始的虚拟地址,首先算出页目录的虚拟地址,其实根本不用算,将B2,B3置0即是,即B1|0|0,那么页表开始的虚拟地址就是:B1|0|0-1K*(B3-1)。我们在Windows中看到的0XC0300C00这个自映射页目录项地址只是一个实现特例,实际上其它的地址也是可以的!
2.2.神奇的宏
有了上述的基础,那几个神奇的宏就不在话下了。说白了就是页表虚拟地址三元组和虚拟地址空间页面虚拟地址三元组之间的关系。分析一下页表项的虚拟地址,想找到它的物理地址,首先要找到它的页表,由于页表的页表是页目录,因此页表项的高10位一定定位到了页目录,也就是第B1个页表上,接下来中间10位索引到了该页表项所在的页面,即第B2个页表所在的页面,低10位B3给出该页表项在该页面的偏移,最终定位到物理位置。由最上面的Windows虚拟地址的线性映射图可以看出,每一个页表对应1K个页面,那么第N个页表一定可以索引从N*1K页面到(N+1)*1K之间这1K页面中的所有页面,具体哪个页面可以通过页表项虚拟地址的B3部分给出。B3的含义是该页表项虚拟地址从该页表开始处虚拟地址的字节偏移,这个偏移按页面为单位就是1K页面中该页表项指向的页面的虚拟地址的B2部分!因此就可以直接导出:
MiGetVirtualAddressMappedByPte(PTE)
这个宏定义了!一切就在上面那幅虚拟地址线性映射的图!
由此可见,所有的所谓奇妙的东西都是Windows虚拟内存布局设计的自然而然的结果。Windows每进程独享全部虚拟地址空间的设计推演出规整的地址空间布局,从而为在内核中或者其它进程中高效方便且直接的操作页表内容以及访问内核虚拟地址空间中的页面。
然而如果按照一种正常的方法,则页表属于MMU机构,并不对进程开放,因此也不必映射在虚拟地址空间。但是由于操作系统操作的地址都必须是虚拟地址,所以在操作页表本身的时候就会陷入两难境地,因此就有一种临时映射的方案,即在操作页表的党史将页表所在页面临时映射为一个虚拟地址空间的虚拟地址,操作完毕后立即解除映射,这种方式很自然,但是并不绝对,毕竟它迎合了一种论调,即页表不必处在地址空间,除了MMU机构以及其代理,没有谁会触及页表。
在继续同样冗长的Linux内存布局之前,我来总结一下Windows的方式,记住,自映射以及神秘宏都不是什么了不起的,其背后的根本就是Windows采用了独占的虚拟地址空间的方式,既然采用了这种方式,接下来自然而然的结论就是所有页表会被安排在一个连续的虚拟地址段,由它们来映射整个虚拟地址空间,最终的结论就是自映射!既然页表的虚拟地址是连续的,页目录作为页表的页表也处在其中并且被分配了物理页面,CR3寄存器指向了该页面的物理位置。既然被分配了物理页面,一定有一个页表项指向了它,该页表就是这个页目录本身,这就是自然而然的自映射。最终有了这些之后,那些被认为i神秘的宏也不再神秘。
Windows统一且规则的虚拟地址空间管理直接影响到了其内存管理API接口,大家应该都知道Windows内存分配的保留,提交两阶段吧!
Linux采用了一种截然不同的方式对待页表以及其它的管理机构。我们将看到,地址空间布局的不同,将会多么激烈得影响内存映射的方式啊!