[转载]Windows下的分页模式- 页目录和页表从物理内存到虚拟映射求值

标 题: 【原创】Windows下的分页模式-  页目录和页表从物理内存到虚拟映射求值

作 者: hrpirip

时 间: 2012-12-06,12:45:36

链 接: http://bbs.pediy.com/showthread.php?t=159554

昨天在网上看到一段代码令大为不解,大家都知道一个虚拟地址到物理地址的转换伪公式为:
*(*(*PD[(VirtualAddress>>22)] & FFFFF000) [(VirtualAddress & 3FF000)] )&FFFFF000 + VirtualAddress & FFF;
PD为页目录基址,
*PD[(VirtualAddress>>22)]  主要算出页表的物理页号(即定位哪一张页表)后面是属性位,而后& FFFFF000获得该页表所在的实际物理地址。
而后以页表的实际物理地址牵引[(VirtualAddress & 3FF000)] 即定位该页表的哪一个页表项,得到虚拟地址所在页的物理页号和一些属性位。而后将其)&FFFFF000,即得到所在页的物理地址,最后再加上虚拟地址的低12bit(即 VirtualAddress & FFF),即为页内偏移,从而得到真实物理地址。

后来才发现原来伪公式是针对你可以直接访问物理地址而言的,并不能直接使用,在Windows保护模式下实际物理页表和物理页目录都被映射到进程的地址空间了,每个进程都有自己的页目录和页表,以此进行隔离进程。因此在内存空间中对页目录和页表的访问应当以虚拟地址的方式进行访问。网上摘录代码如下:

代码:

unsigned int PDE;
unsigned int PTE;

if (VirtualAddress >= 0x80000000 && VirtualAddress < 0xa0000000)
{
    PhysicalAddress = VirtualAddress - 0x80000000;
}
else
{
    PDE = * (unsigned int *) ( (VirtualAddress >> 22) * 4 + 0xC0300000);
    if (PDE & 0x00000001)
    {
        PTE = * (unsigned int *) ( (VirtualAddress >> 12) * 4 + 0xC0000000);
        if (PTE & 0x00000001)
        {
            PhysicalAddress = ( (PTE & 0xFFFFF000) + (VirtualAddress & 0x00000FFF) );
        }
    }
}

普及一下:
0xC0300000 2k以后页目录即被映射到这个地址上,页目录本身为一页,具有1024个页目录项,每一项为4字节,整个页目录的大小为4k。每一个页目录项都描述一张页表。(当页目录项的P位为1的时候)
0xC0000000 2k后所有的页表都将从该地址开始映射,每4kb为一个页表,每个页表1024个项,每项描述4k的内存页,因此每个页表应当描述: 1024个页表项*4kb = 4M ,4M大小的内存,共1024张页表,因此这些页表描述了整个4G内存地址空间。而每个页表项与页目录项一样,仅占用4个字节的地址空间,因此每一张页表的占内存的大小为: 1024个页表项*4字节 = 4KB。

接下来看一句:

代码:

if (VirtualAddress >= 0x80000000 && VirtualAddress < 0xa0000000)
{
    PhysicalAddress = VirtualAddress - 0x80000000;
}

首先判断虚拟地址是否为80000000 - 9FFFFFFF范围内的地址,如果是,则直接得其偏移作为物理地址。
这是因为2k下地址空间 80000000 - 9FFFFFFF 总是和物理内存 00000000 - 1FFFFFFF 一一映射,所以只要直接减去0x80000000得到的偏移即为物理地址(这一段内存叫做4M大内存页,每一页为4M,而不是4kb,描述这段内存不再使用页表,而是直接用页目录中的物理页号进行定位,一般供内核使用,以防止过多的占用Tlbs总存放的页表项。)

代码:

PDE = * (unsigned int *) ( (VirtualAddress >> 22) * 4 + 0xC0300000);

这一句也很好理解。即计算出PDE的地址(物理地址)。
而后代码中转,直接抛弃这个PDE了,当时大为不解,为哈计算个PDE后就丢了呢。 原来是这样的。
前面已经解释过了,物理地址是不能直接访问的。也就是这个所在页表的起始物理地址(PDE),是不能访问的,因此我们没有办法通过得到的PDE地址去访问一个页表。既然页表都访问不了,那么久不可能获得一个PTE。
但是可以看到,作者将其中的PTE获取方式写为:

代码:

PTE = * (unsigned int *) ( (VirtualAddress >> 12) * 4 + 0xC0000000);

并没有借助到PDE,那么这是怎么计算的呢?

这时候就应该开始变通了。看如下图:
                  
我们可以看到:
当虚拟地址寻址页目录为第0项的时候,其页表地址其实是从第0张页表开始的,
当虚拟地址寻址页目录为第1项的时候,其页表地址其实是从第1张页表开始的。
当虚拟地址寻址页目录为第2项的时候,其页表地址其实是从第2张页表开始的。

接下来我们可以得知:
当虚拟地址寻址页目录为第0项的时候,其页表地址实际跨越了0个页表项,
当虚拟地址寻址页目录为第1项的时候,其页表地址实际跨越了1024个页表项。(跨过了第0张页表的1024个页表项)
当虚拟地址寻址页目录为第2项的时候,其页表地址实际跨越了2048个页表项。(跨过了第0张页表的1024个页表项和第1张页表的1024个页表项)

现在我们来试着计算一个地址: 
假设地址: 0x00401000
该地址二进制形式为:10000000001000000000000
取其高10bit 0000000001   (页目录牵引第一项)
取其中10bit 0000000001   (页表牵引  第一项)
其低12bit为0,即页对齐,这里为了简单点说明,因此没有页内偏移。

我们按照上面得出的结论,以页表项为最小单位进行计算,0000000001为第一个页目录项,即其指向了第一张页表,针对所有页表的总基址而言,这张页表的所处位置已经跨越了1024个页表项(属于第0张页表的1024个页表项),所以我们可以写出:
0000000001 * 1024个页表项 * 4(每个页表项4字节) = 4096个字节。
即相对于所有页表的总基址0xC0000000的偏移。

现在我们得到了页表的地址,接下来再看中10bit 的0000000001,其含义为我们需要的是这张页表中的第1项,因为不是使用第0项,所以我们略过第个0页表项所占用的4个字节。取第一项即得到该虚拟地址真实所在的内存页物理地址。

总结为:
页表项相对页表总基址的偏移为 = 4096个字节 + 4个字节 = 4100个字节。
好,接下来我们用作者的方式来计算这个偏移:
0x00401000 右移 12位后 等于 0x401
0x401 * 4 = 0x1004 = 4100个字节。

结果是很明显的,是正确的。那么为什么我们上面计算那么多,而作者仅依靠0x00401000右移12位后再乘以4就可以得到所要使用的页表项相对于所有的页表总基址的偏移地址呢?
答案说难也不难,说简答也不简单。关键在于理解:
既然说0x00401000 右移 12位后 等于 0x401 ,再乘以4字节后即为所需要的页表项相对于所有页表总基址的偏移,那么可以说明这个0x401 即为所跨过的所有页表项的个数。

我们来看一下 这个0x401 的二进制形式: 10000000001  
去掉最尾部的1 ,可以看到是一个10000000000转换为 10进制为 1024,刚好为所跨越的第0张页表的1024个页表项的个数。 后面的1即为页表内第一个页表项。

可以得知的是,(VirtualAddress>>12) 右移12位后将虚拟地址中的页目录项牵引号和页表项牵引号都算进去了,即:使用了哪一张页表和使用了哪一个页表项。

接下来的事情我没有办法用专业语言来描述,因为我是个17岁的中专生也是半路出家自学电脑的,没有什么高等数学基础,因此只能笼统的说一下结论,或者我补习后回来补充:

虚拟地址转换为二进制后:
   页目录牵引号经过右移12位就等于乘以十进制的1024(比如牵引1位移12位就是10000000000?1024个页表项,牵引2位移12位后就是100000000000?2048个页表项),也就可以直接通过位移计算该页表总共跨越过多少个页表项,以此求出要找的那个页表偏移。

而后只要用这个页表偏移值加上虚拟地址中的页表项偏移值,即可得到我们需要的页表项相对所有页表总基址的偏移。
也就是说0x401就是位移的结果: 10000000001 = 10000000000b + 1b
和初中公式:x * y + z * y = (x+z) * y 的原理是一样的,这也是为什么最后这个0x401可以代入取址公式的原因 401*4 = (10000000000b + 1b) *4字节 = 4096字节。
最后再用这个值加上0xC0000000就可以获得PTE的虚拟地址,直接*PTE + page_offset(12bit) 即得到了虚拟地址所对应的最终物理地址。

也不得不佩服Unix和微软的人才,分页机制涉及了很多"巧合"与"契合",却也很合理

时间: 2024-08-05 17:36:48

[转载]Windows下的分页模式- 页目录和页表从物理内存到虚拟映射求值的相关文章

如何在Windows下用cpu模式跑通py-faster-rcnn 的demo.py

关键字:Windows.cpu模式.Python.faster-rcnn.demo.py 声明:本篇blog暂时未经二次实践验证,主要以本人第一次配置过程的经验写成.计划在7月底回家去电脑城借台机子试试验证步骤的正确性,本blog将根据实际遇到的问题持续更新.另外blog中除提到的下载链接外我还会给出网盘链接方便下载,包括我的整个工程的网盘链接.如果有些报错解决不了可直接拿本人的相关文件替换,本篇blog具有较高的参考性. 本人微软版caffe工程     下载链接:http://pan.bai

[转载]windows下mongodb安装与使用整理

windows下mongodb安装与使用整理 一.首先安装mongodb 1.下载地址:http://www.mongodb.org/downloads 2.解压缩到自己想要安装的目录,比如d:\mongodb 3.创建文件夹d:\mongodb\data\db.d:\mongodb\data\log,分别用来安装db和日志文件,在log文件夹下创建一个日志文件MongoDB.log,即d:\mongodb\data\log\MongoDB.log 4.运行cmd.exe进入dos命令界面,执行

Windows下的上帝模式

Windows 系统中隐藏了一个秘密的"God Mode",字面上译为"上帝模式". 可以看到这里包括了方方面面的系统设置选项和工具,而且每一个项目所对应的功能也都清晰显示,一眼明了,使用起来非常方便. 唤出方法: 1.在桌面空白处右键新建文件夹. 2.文件名输入GodModel.{ED7BA470-8E54-465E-825C-99712043E01C},"."之前的文字可随意输入,"{}"中的英文和数字必须正确输入. 3.

转载(windows下安装mysql)

转载请声明出处:http://blog.csdn.net/u013067166/article/details/49951577             最近重装了系统,去MySQL官网下载了最新的MySQL5.7.9,我选择的是解压版,安装之后启动服务的时候,提示服务无法启动,在网上找了很多教程,弄了很久都没有弄好,后来还是决定去英文官网找找答案,终于在官网发现了这个:   As of MySQL 5.7.6, the Zip Archive no longer includes a data

用windows下的上帝模式隐藏文件

所需工具 一行字符串: .{ED7BA470-8E54-465E-825C-99712043E01C} (全部都需要,包括点点和括号) 图文说明 「上帝模式」,很多人不知道有这个东西,所谓的「上帝模式」就是列出了所有的 Windows 的设置项,包括Internet选项.文件夹选项等等都有,我们想要设置什么打开「上帝模式」解决了,非常方便的一个东西. 01 开启「上帝模式」 首先随便新建一个文件夹,然后给文件夹重命名,在文件夹名字后面加上那串字符串:".{ED7BA470-8E54-465E-8

把windows下的压缩包放到Linux目录下去

今天在自学redis时出现了问题,因为楼主linux也是空白纸,前几天安装了Linux后就只会基本的命令,其他的一概不通啊,所以当redis要在Linux中用时就傻眼了,索性就在windows中下载了redis,再想办法把它拷贝到Linux中即可   那种从windows把war包拉到Linux中的软件我忘记了是啥,所以就在网上翻翻看看寻找其他的方法,这个方法我试成功了,拿来分享 废话不多说开始啦: 一.在网上下载putty,压缩包解压后里面一定要有pscp.exe,如下图 二.把putty的解

windows下使用tree工具打印目录树

转自原文Window平台下tree 命令使用,19.8 Tree for Windows工具: 打开进入 Tree for Windows 页面,选择下载 Binaries zip 文件. 解压压缩包,找到压缩包内的 bin 目录,将 bin 目录下的 tree.exe 复制 找到 C:\\Program Files\Git\usr\bin 目录(或者放入git  bin目录,看个人爱好),将 tree.exe 粘贴到该目录下,安装即完成 效果如下: E:\Projs\algs\xxxx\ins

windows下vim 块模式问题

VIM: gvim 使用 Ctrl+V 發表於 2005 年 10 月 27 日 由 Tsung vim 要做垂直選取的動作, 就要使用 "Ctrl + v", 但是 gvim 會遇到一個問題, 就是使用 "Ctrl + v" 時, 會變成貼上. 後來經過高人指點, gvim 是在 Windows 中另外再 maping 那些快速鍵的(Gnome 中應該也是一樣意思, 只是改的檔案應該不同). 修改 vim/vim63/mswin.vim, 修改如下: "

[转载]windows下安装Python虚拟环境virtualenvwrapper-win

1 前言 由于Python的版本众多,还有Python2和Python3的争论,因此有些软件包或第三方库就容易出现版本不兼容的问题. 通过 virtualenv 这个工具,就可以构建一系列 虚拟的Python环境 ,然后在每个环境中安装需要的软件包(配合 pip 使用),这一系列的环境是相互隔离的.作为一个独立的环境就不容易出现版本问题,还方便部署. 2 安装 pip install virtualenv 3 virtualenv的基本使用 3.1 创建虚拟环境 virtualenv venv