虚拟内存到物理内存(32位)

【原文:http://www.cnblogs.com/DylanWind/archive/2010/09/05/1818685.html

部分出处(http://blog.sina.com.cn/wyw1976) 和 http://www.cnblogs.com/Winston/archive/2009/04/12/1434225.html

我们通过一个实际的例子,窥看操作系统是如何完成从逻辑地址到物理地址的转换的。

(1)运行计算器程序(calc.exe),随便输入任意数字,如下图所示:

image

(2)运行WinDbg, File-->Attach to a process, 选择calc.exe

(3)输入命令"x calc!g*", 列出calc中所有以g开头的符号,如图

image

其中的gpszNum存放的就是用户输入的数值

(4)输入命令 "db poi(calc!gpszNum)", 因为gpszNum是一个指针变量,因此用取值操作poi,结果如下:

 image 
我们看到了我们在界面上输入的数字。

(5)另一种方法:既然gpszNum是一个指针,那就意味着gpszNum中存放的是一个地址,而该地址指向的内存中包含的才是我们要找的值,如下图(dd 命令用于读取指定内存地址中的值):

image

dd gpszNum 也可以直接 dd 01014db0 
我们仍然找到了字符串“888652”,它所在的逻辑地址是000af360, 这显然是一个用户态地址,因为在Windows平台上,每个进程的地址空间是0~4G, 其中0~2G是用户态,而2G~4G是内核态。那它对应的物理地址是多少呢?这就涉及到逻辑地址到物理地址的转换。

Virtual Address(虚地址)

对于一个32bit的virtual address,其格式如下所示:

image

其中高10bit为Page Directory中的index项,中间10Bit为Page Table的index项,最低12bit为页内偏移地址。注意,它们记录的都是“偏移”量。

从virtual address向physical address转换的过程如下:

Step1: 通过CR3寄存器定位到页目录的起始位置(DirBase), 故CR3 Regesiter又称为页目录基地址寄存器; !Process 0 0可以看到每个进程的DirBase

Step2: 取virtual address的高10bit作为index,在PD中查找相应的PDE. 每个PDE的高20位代表该PDE所指向页表起始物理地址的高20位; 一般都是000000000,所以取!DirBase的第一项作为PDE;

Step3: 根据PDE中的页表基地址(即PDE的高20bit)定位到Page Table(页表); 一般step3和step4一起 !(PDE前20位 + 10-21bit索引*4),得到PTE

Step4:  取virtual address中第10-21bit作为索引,选取页表中的一个PTE(页表项),每个PTE的高20位代表的是4KB内存页的起始物理地址的的高20位

Step5: 取PTE中的内存页表基地址(高20bit)+ virtual address中的低12位offset,即可以得到实际的physical address 了; !PTE前20位+低12位offset

image

有关Paging的二级寻址, 
总是为每个进程分配一个页目录page directory, 如果系统中有多个进程,内存中就会有多个页目录,每个page directory本身占用4K。 
先找page directory,有1024项目,每一项对应一个page directory entry(通过线性地址的高十位) 
再从page table中找PTE,每个PTE对应一个页面文件 4K(有4k,2M, 4M)。( 通过线性地址的中10位)每个page table本身占用4K 
所以一张Page table对应的内存空间是 4K * 1024 = 4M , 因而Page directory对应大小是1024 * 4M = 4G ,虚拟内存大小4G, 其中0~2G是用户态,2G~4G是内核态

有关逻辑地址,线性地址和物理地址 
程序中变量或函数的逻辑地址是在程序编译确定的,实际上就是段内的偏移。 
逻辑地址加上段地址,就形成了线性地址。 

在Windows平台上,逻辑地址就是线性地址。现在我们通过WinDbg来验证。

(1)打开WinDbg, 点击File----->Attach to a Process...,关联任何一个进程,WinDbg会显示段寄存器的值。

(2)执行“dg + 段选择子”, 可以查看段的详细信息,如下图所示: 
image

其中base指定了段的基地址,可以看到代码段CS, 数据段DS的基地址是0,这也就是意味着逻辑地址+0=线性地址,因此逻辑地址==线性地址。也就是Windows在淡化段地址,真正其作用的是页地址。 

另外,上图中还有一个fs==0038是什么意思呢?在Windows平台上,该寄存器存放的是当前线程的TEB(线程环境块),而它的基地址是非零的,验证如下:

(1)执行"dg 0038", 可见其base为7ffdc000

(2)执行"!teb",显示当前线程的TEB信息,可以看到其存放地址确实是7ffdc000,如下图所示:

image 
32位线性地址分为3段:

31----22:高10位指定了页目录偏移,系统在加载每个进程的时候都有为该进程分配内存,而内存的管理是通过三级页表的方式,但并不是所有内存都一步分配到位的。首先,总是为每个进程分配一个页目录,它的大小是1024,高10位就是指定了1024项中的一项,每一项其实也是一个内存地址,该地址开始的1024个空间代表一个页表。只要进程是活着的,这个页目录就一直存在内存中,如果系统中有多个进程,内存中就会有多个页目录在每个进程的进程环境块(PEB)中会记录页目录的地址,在进程切换的时候,该地址会传递给CPU的CR3寄存器以便CPU进行地址转换。在WinDbg中,我们可以看到该信息:

(1)打开WinDbg, 点击File---->Kernel Debuging...,选择Local, 进入内核调试模式

(2)在WinDbg窗口中执行".symfix c:\111",以及“.reload”, 加载符号表

(3)运行"!process 0 0", 列出了当前系统中所有进程,其中的DirBase就是页目录地址。

 image 

21----12:中10位指定了页表偏移通过高10位可以得到一个页表,该页表也有1024项,通过中10位在这1024项中选择1项,每一项包含一个地址,该地址开始的4096个字节就是一页。页表要占用内存,而且随着程序所需内存的变化而变化。

11----0:低12位指定了页内偏移通过中/高两项,我们可以定位一个页,每页大小为4096,是一个连续空间,低12位就是具体地址了4096中的一项,也就是具体的一个物理内存了。

一般的,地址转换的步骤是逻辑地址---->线性地址---->物理地址

 

从最后一步开始,执行du, 然后执行".format", 这条命令将显示该地址的二进制形式,然后我们按照线性地址的组成,分为10-10-12的形式, 如下如所示:

 image 
页表项偏移: 00000000,  即第一项

页表偏移:  0010101111,  即为0xAF

页内偏移:  0011 01100000,即为0x360

(2) 再启动一个WinDbg, 点击File--->Kernel Debug..., 选择Local, 进入内核调调试模式

(3) 执行“!process 0 0”, 找到计算器进程,如下图所示,其DirBase=3f74a000, 这就是页目录所在的地址。

image

(4) 执行“!dd 3f74000”, 读取页目录, 
image 
因为线性地址的页表项为0, 就对应的第一项,即64db6867, (注意, 页表项的高20位即64db6000代表页表地址,低12位为属性位)

(5)页表地址64db6000代表一个页表的起始地址。从该地址开始的内存中的每一项(4个字节)对应一个PTE,因为指针占用是4个字节,所以要用偏移量乘以4。因此该起始地址加上页表偏移(0xAF),就可以找到PTE。执行“!dd 64db6000 + AF*4”,如下图所示:

image

(6)上图中的1589867的高20位即(0x15894000)就是物理页的其实地址,因为每页的大小是4K,所以页地址的低12位应该为0,之所以不为0,是充分利用这些位存放flag,例如标志是否可读,是否在内存中等等。页的其实地址加上页内偏移(360),就是物理地址,从下图可以看出,该地址存放的就是我们要找的字符串。

 image 
至此,我们完成了从逻辑地址到物理地址的转换。

很清晰的过程,但是我没用试验成功,我的环境如下: 
image

image

作者: DylanWind
出处: http://www.cnblogs.com/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类:  Debug

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值