在《JIURL玩玩Win2k内存篇 分页机制 (三)》中提到计算虚拟地址对应PTE地址的公式,如下:

代码:
PTE_Addr = (VirtualAddr >> 12) * 4 + 0xC0000000
从虚拟地址转换到物理地址的过程来看,计算PTE需要虚拟地址的高10位做页目录索引,还需要第12 - 21位做页表索引,上面的公式晃眼看起来,貌似是错的,但是偏偏它又是对的,为什么捏?
其实很简单,因为1024个页表被映射在0xC0000000 - 0xC03FFFFF地址中,暂且不理会页目录被映射到哪,只要知道页目录里的1024个页目录项分别指向0xC0000000 - 0xC03FFFFF地址中的1024个页表就行了(其实这是因为页目录和一个页表完全重合的原因,见《JIURL玩玩Win2k内存篇 分页机制 (四)》),这样就可以得到一个关系,页目录索引n指向的页表就等于0xC0000000 + n * 页表大小,一个页表的大小是1024 * 4  = 4096 = 2的12次方,得到了页表,在加上页表索引就可以得到页表项(PTE)的地址了,用公式表示就是如下:

代码:
//为了缩短表达式,用VA表示虚拟地址
//           页目录索引                  页表索引
PTE_Addr=((VA&0xffc00000)>>22)*4096+((VA&0x3ff000)>>12)*4+0xc0000000
        =(((VA&0xffc00000)>>22)<<12)+(((VA&0x3ff000)>>12)<<2)+0xc0000000
        =((VA&0xffc00000)>>10)+((VA&0x3ff000)>>10)+0xc0000000
        =((VA&0xffc00000)+(VA&0x3ff000)>>10)+0xc0000000
        =((VA&(0xffc00000|0x3ff000))>>10)+0xc0000000
        =((VA&0xfffff000)>>10)+0xc0000000
        =(((VA&0xfffff000)>>12)<<2)+0xc0000000
        =((VA&0xfffff000)>>12)*4+0xc0000000
由于VA的低位被清零,所以上面的移位操作化简是成立的。最后的一个表达式已经很明显了,VA & 0xfffff000完全是多余的,因为始终要>>12,所以最终公式就是:

代码:
PTE_Addr = (VirtualAddr >> 12) * 4 + 0xC0000000
combojiang兄在《透过MmIsAddressValid看Windows分页机制》中也提到PTE的计算公式,如下:

代码:
PTE = ((VA >> 12) << 2 ) & 0x3FFFC + 0xc0000000;
估计combojiang兄深夜发帖,意识模糊 :-),少写了个F,误导了不少群众,还没人指出,正确的应该是:

代码:
PTE = ((VA >> 12) << 2 ) & 0x3FFFFC + 0xc0000000;
这里的& 0x3FFFFC也是不必要的,& 0x3FFFFC只是将低2位清零,而<< 2后低2位本来就是0,所以等价于:

代码:
PTE = (VA >> 12) * 4 + 0xc0000000;
上面说的都是在32位 x86 非PAE的情况下,PAE情况下略有不同,有时间再整理