在上一篇中我们分析了 linux 在 x86-32 模式下的虚拟内存映射流程,本章主要继续分析 linux 在 x86-64 模式下的虚拟内存映射流程。
讨论的平台是 x86-64, 也可以称为 AMD64, IA-32e, 是现在广泛使用的 64 位架构,可以向前兼容 16位和 32 位的 x86. 另外一种独立的 64 位架构 IA-64与现有架构不同而且貌似发展不好,一般是接触不到的,我们平常讨论的 64 位基本就是指 x86-64.
现在的 CPU 基本都是支持 64 位的,根据处理器强大的兼容性,我们可以配置为 long mode 和 legacy mode, 根据安装的操作系统的模式可以使用不同的模式。
从 wikipedia 的截图可以看出, 我们下面要测试的是 Operating mode = Long mode, Operating sub-mode = 64-bit mode 下的虚拟内存映射流程。
映射流程简述分段的存在更多就是为了兼容性,所以在 x86-64 下的 64位程序该功能近似于 bypass。处理器默认 CS, DS, ES, SS的段基址为 0,所以我们下面就不讨论逻辑地址到线性地址的转换了,因为基址为0,经过运算后线性地址和逻辑地址是一样的,和上一章的扁平模式一样。
分页过程会将 48-bit 的线性地址转换为 52-bit 的物理地址, 可以看出虽然是 64bit 的操作系统但在处理器层面并没有提供 2^64 大小的访问范围。48-bit 线性地址可以有以下 3 种映射分配.
4-KByte 页面
2-MByte 页面
1-GByte 页面
我们暂时还不知道 linux 使用哪种分页,但是知道了每种模式下各个寄存器和page structure entry的格式,可以下面慢慢分析。
格式
验证方案本文整个流程参考了网上的另一篇文章,我会在文章末尾列出链接。
整个验证流程和上一篇在 x86-32 下的测试流程一样,这里就不说明了。
验证过程编译加载编译文件,加载 sys_reg.ko, phy_mem.ko 模块
运行 running-PRog运行后可以得到以下输出:
可以看到变量 a, 这就是我们要寻找物理地址的变量,我们给变量 a 赋了个特殊值方便确认。由于我的操作系统和 running-prog 都是64位的,所以对 a 的地址翻译是遵循 x86-64 下的机制的。a 的逻辑地址已经打印出来,而且也就是 a 的线性地址,我们先将 48bit 的线性地址分段.
PML4控制寄存器 CR3 存储的是 PML4 的基址, bits 47~39 为 PML4E 的序号,对应的 PML4E 地址为:
0x275A1000 + 0 * 8 = 0x275A1000
PML4E 的值为 0x275DA067.
PDPTPDPT 的基址为 0x275DA000, bits 38~30 为 PDPTE 序号,计算出的 PDPTE 地址为:
0x275DA000 + 0 * 8 = 0x275DA000
PDPTE 的值为 0x623A4067, bit7 = 0 说明指向的是 page directory.
PDPD 的基址为 0x623A4000, bits 29~21 为 PDE 的序号,计算出 PDE 的地址为:
0x623A4000 + 3 * 8 = 0x623A4018
PDE 的值为 0x692BB067, bit7 = 0 说明指向的是 page table.
PTPT 的基址为 0x692BB000, bits 20 ~12 为 PTE 的序号, 计算出 PTE 的地址为:
0x692BB000 + 1 * 8 = 0x692BB008
PTE 的值为 0x800000004AD6F867.
page framepage frame 的基址为 0x4AD6F000, bits 11~0 为在 page frame内的偏移,计算出变量的物理地址为:
0x4AD6F000 + 120 = 0x4AD6F078.
熟悉的 0xA5A5AA550000FFFF, 说明我们找到了变量 a 的实际物理地址。
结束感谢 Linux内核在x86_64 CPU中地址映射 一文,我的整个流程参考了原作者的文档和代码, 再次感谢原作者的分享。
下面是源代码链接.study-linux-vm-64bit
新闻热点
疑难解答