步骤 5 / 9
动手:把一个虚拟地址拆成 4 级索引
48 位虚拟地址 = 4 段 9 位索引 + 12 位页内偏移;0x7ffd1234abcd 拆出来是 255、500、145、330 和 0xbcd。
x86-64 的 4 级页表只用虚拟地址的低 48 位(更高的 16 位必须和第 47 位相同)。为什么是 9 和 12? 页大小 4KB = 2^12 字节,所以最低 12 位是页内偏移,翻译时原样保留。 每张页表本身也正好占一页:4096 字节 ÷ 每项 8 字节 = 512 项 = 2^9,所以每一级用 9 位当下标。12 + 9 × 4 = 48。 在右边输入地址 0x7ffd1234abcd(这是一个典型的栈地址,栈在用户空间顶部附近): 第 47–39 位 = 255,是第 1 级(PML4)的下标;第 38–30 位 = 500,第 2 级(PDPT);第 29–21 位 = 145,第 3 级(PD);第 20–12 位 = 330,第 4 级(PT),这一项里存着物理页号;最低 12 位 = 0xbcd,即页内第 3021 字节。 MMU 从 CR3 寄存器拿到第 1 级表的物理地址,用 255 找到下一级表,再用 500、145、330 依次往下,最后把物理页号和 0xbcd 拼起来就是物理地址。 可以试试:把地址加 1,只有偏移变化;加 0x1000(一页),第 4 级下标变成 331;换一个很小的地址如 0x400000(传统的程序代码起点),看前两级下标都是 0,和栈共用的表很少,这就是多级页表省空间的原因。