在Windows的虚拟内存管理方案中,有一个设计值得特别一提,那就是Windows页目录自映射机制。Dave Probert很早在一份讲义中提到了这一机制(称为self-mapping page tables),并且给出了清楚的解释(http://i-web.i.u-tokyo.ac.jp/edu/training/ss/msprojects/data/07-ProcessesThreadsVM.pdf,2004年7月)。
首先,看下面的一个宏:
#define MiGetVirtualAddressMappedByPte(PTE) ((PVOID)((ULONG)(PTE) << 10))
此宏的含义是,给定一个PTE的虚拟地址,返回该PTE所指页面的虚拟地址。
举例而言,假设PTE的地址(虚拟地址)为0xc0390c84,即1100,0000,00|11,1001,0000,|1100,1000,0100,这里“|”符号将它分为页目录索引、页表索引、页内偏移三部分。在Windows中,页目录(CR3寄存器)的地址是0xc0300000,所以,处理器在访问该PTE的时候,首先从页目录页面中,找到1100,0000,00项,即第0x300项。这一项由系统特别设置好,它指向页目录自身(后面进一步解释为什么这么设置)。接下来查找0xc0390c84的页表索引,即11,1001,0000,处理器继续在页目录页面中查找,找到11,1001,0000,即第0x390项,这一项指向一个页表页面。最后,处理器再根据页内偏移1100,1000,0100,即0xc84,定位到第0x321项。此PTE的寻址过程如下图所示。
按照MiGetVirtualAddressMappedByPte,将0xc0390c84,左移10位,变成0xe4321000,即1110,0100,00|11,0010,0001,|0000,0000,0000,这样得到页目录索引1110,0100,00,即0x390;页表索引为11,0010,0001,即0x321;页内偏移为0。可以看到,这里复用了PTE寻址过程中的两次查表步骤。此PTE所指页面的寻址过程如下图所示。
MiGetVirtualAddressMappedByPte宏之所以能够工作的关键之处在于,页目录页面(虚拟地址为0xc0300000)的0x300项指向其自身。即下图的结构。
由此可以明白,页目录地址0xc0300000和这里的0x300项绝不是偶然的,而是精心选择的(但并非唯一)。
我在阅读Windows内核中一段分配系统PTE的代码时,看到了MiGetVirtualAddressMappedByPte宏,百思不得其解。经过两个晚上的苦思冥想,才领悟到上述寻址结构的奥妙所在。后来整理成“Windows内核原理与实现”一书中关于Windows页目录自映射方案的描述(见第202-204页)。再后来,偶然的机会看到Dave Probert的讲义中早有提及,印证了自己的想法。
本文转自:http://blog.csdn.net/panaimin/archive/2010/10/02/5918359.aspx
《Windows内核原理与实现》豆瓣链接:http://book.douban.com/subject/4719159/
互动网购买链接:http://www.china-pub.com/196542