
页表的虚拟地址:直接映射与递归映射
操作系统只能通过虚拟地址访问页表来修改页表。假设页表在物理内存中的位置已经确定,问题就是:内核该用哪个虚拟地址访问它?
一张页表占一个页框。先找到页表页的虚拟起始地址,才能在页内找到某个页表项的虚拟地址。下面分别看直接映射和递归映射。递归映射的推导限定在 x86-64 四级页表、4 KiB 页、每级 512 个 8 字节表项 的情形。
直接映射下页表页的虚拟地址
内核把物理内存按固定偏移映射到虚拟地址空间。设偏移为 base,某级页表位于物理页框 p,那么这张页表页的虚拟起始地址就是 base + p。内核可以把它当作页表数组的首地址。
直接映射下页表项的虚拟地址
一张页表中每项占 8 字节,因此第 i 个页表项的虚拟地址是 base + p + 8i;在 C 中就是 ((uint64_t *)(base + p))[i]。读取该项得到下级页表的物理地址后,再加 base,便得到下级页表页的虚拟地址。
下面的示意代码从已知的顶级页表物理地址 root_pa 出发,沿目标虚拟地址 va 的索引逐级访问,最后得到一级页表项的指针:
unsigned idx[4] = {
(va >> 39) & 0x1ff, (va >> 30) & 0x1ff,
(va >> 21) & 0x1ff, (va >> 12) & 0x1ff
};
uint64_t *table = (uint64_t *)(base + root_pa);
for (int level = 0; level < 3; ++level) {
uint64_t next_pa = table[idx[level]] & 0x000ffffffffff000ULL;
table = (uint64_t *)(base + next_pa);
}
uint64_t *pte = &table[idx[3]];循环中,table 是当前页表页的虚拟起始地址,table[idx[level]] 访问该页表中的一个表项。表项给出下级页表的物理地址 next_pa,下一轮再用 base + next_pa 访问下级页表。
递归映射下页表页的虚拟地址
递归映射在四级页表(PML4)中选一个索引 R,让 PML4[R] 指向 PML4 自己的物理页框。这样便能利用地址中的索引,把各级页表映射到一组可计算的虚拟地址。
假设普通虚拟地址 x 的四级索引依次是 A、B、C、D。它通常走过:
CR3 → PML4[A] → L3[B] → L2[C] → L1[D] → 数据页构造索引为 R、A、B、C 的新虚拟地址后,实际到达的页框变成:
CR3 → PML4[R] → PML4[A] → L3[B] → L2[C] → L1 表本身第一次查表绕回 PML4,使后续查表少向下走一级。最终被映射为数据页的便是 x 对应的一级页表。将递归索引重复两次、三次、四次,同理可访问二级、三级、四级页表。
用八进制表示地址:AAA、BBB、CCC、DDD 分别是 x 的四级索引,每组三位八进制数对应 9 位;RRR 是递归索引。SSSSSS 的设置与 x86 的地址要求有关,这里先忽略。把页内偏移设为 0000,各级页表页的虚拟起始地址就是:
| 页表页 | 地址结构(八进制) |
|---|---|
| 一级页表 | 0o_SSSSSS_RRR_AAA_BBB_CCC_0000 |
| 二级页表 | 0o_SSSSSS_RRR_RRR_AAA_BBB_0000 |
| 三级页表 | 0o_SSSSSS_RRR_RRR_RRR_AAA_0000 |
| 四级页表 | 0o_SSSSSS_RRR_RRR_RRR_RRR_0000 |
递归映射下页表项的虚拟地址
页表页的虚拟起始地址确定后,访问其中第 i 个表项只需加 8i 字节。设 EEEE 是普通页中的偏移,各级页表项的虚拟地址可写成:
| 虚拟地址 | 地址结构(八进制) |
|---|---|
| 页 | 0o_SSSSSS_AAA_BBB_CCC_DDD_EEEE |
| 一级表项 | 0o_SSSSSS_RRR_AAA_BBB_CCC_DDDD |
| 二级表项 | 0o_SSSSSS_RRR_RRR_AAA_BBB_CCCC |
| 三级表项 | 0o_SSSSSS_RRR_RRR_RRR_AAA_BBBB |
| 四级表项 | 0o_SSSSSS_RRR_RRR_RRR_RRR_AAAA |
以一级表项为例,RRR_AAA_BBB_CCC_0000 先定位一级页表页;把索引 DDD 乘以表项大小 8 字节,所得的四位八进制偏移 DDDD 就定位到其中的目标表项。这里的 DDDD 表示 DDD × 8 的结果,例如 DDD = 0o004 时偏移为 0o0040。其余各级表项同理。
下图红色路径对应三级表项,蓝色路径对应四级表项。

参考文献
- Philipp Oppermann, Paging Implementation:物理内存映射、递归映射及地址计算的新版说明。
- Philipp Oppermann, Advanced Paging:递归映射的地址结构。