我们通过一个实际的例子,窥看操作系统是如何完成从逻辑地址到物理地址的转换的。
(1)运行计算器程序(calc.exe),随便输入任意数字,如下图所示:
(2)运行WinDbg, File-->Attach to a
process, 选择calc.exe
(3)输入命令"x calc!g*",
列出calc中所有以g开头的符号,如图
其中的gpszNum存放的就是用户输入的数值
(4)输入命令 "db poi(calc!gpszNum)",
因为gpszNum是一个指针变量,因此用取值操作poi,结果如下:
我们看到了我们在界面上输入的数字。
(5)另一种方法:既然gpszNum是一个指针,那就意味着gpszNum中存放的是一个地址,而该地址指向的内存中包含的才是我们要找的值,如下图(dd
命令用于读取指定内存地址中的值):
我们仍然找到了字符串“888652”,它所在的逻辑地址是000af360,
这显然是一个用户态地址,因为在Windows平台上,每个进程的地址空间是0~4G,
其中0~2G是用户态,而2G~4G是内核态。那它对应的物理地址是多少呢?这就涉及到逻辑地址到物理地址的转换。
有关逻辑地址,线性地址和物理地址
程序中变量或函数的逻辑地址是在程序编译确定的,实际上就是段内的偏移。
逻辑地址加上段地址,就形成了线性地址。
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就是页目录地址。
21----12:中10位指定了页表偏移,通过高10位可以得到一个页表,该页表也有1024项,通过中10位在这1024项中选择1项,每一项包含一个地址,该地址开始的4096个字节就是一页。页表要占用内存,而且随着程序所需内存的变化而变化。
11----0:低12位指定了页内偏移,通过中/高两项,我们可以定位一个页,每页大小为4096,是一个连续空间,低12位就是具体地址了4096中的一项,也就是具体的一个物理内存了。
一般的,地址转换的步骤是逻辑地址---->线性地址---->物理地址