Contents

内存管理:页表(译文)

本文译自 OSDev Wiki 上的一篇文章,原文链接为 Page Tables

页面表(或页映射层级)用于将每个虚拟页映射到对应的物理页。零个或多个虚拟页可以对应同一个物理页。页的大小取决于处理器模式(保护模式、兼容模式或长模式)、所使用的扩展(如 PAE)以及处理器所支持的虚拟地址位数(当前 AMD64 处理器最多支持 48 位虚拟地址)。


Determining your page size

要确定理想的页面大小,不能只看分页结构本身的开销。

通常(对于用户空间),每个进程至少有 3 个具有不同特性的区域:可执行区域、只读且不可执行区域、可读写且不可执行区域。当使用分页来强制这些保护时,从每个区域的实际结尾到下一个页边界之间会产生填充。你可以假设这种填充平均相当于页大小的 50%。例如,对于 4096 字节页面和 3 个区域来说,你可以预计每个进程会因为填充浪费 6 KiB RAM;对于 2 MiB 页面,则每个进程预计浪费 3 MiB RAM。

通常(对于像 Windows 和 Linux 这样的操作系统),大约有 50 个进程,其中大多数使用非常少的 RAM,而一些使用很多(如 X、浏览器等)。如果假设 50 个进程每个平均使用 10 MiB RAM;那么(在长模式下使用 4 KiB 页时)每个进程(平均)会使用一个 PML4、一个 PDPT、一个 PD 和 5 个页表;即总共约 32 KiB 的分页结构。而对于 2 MiB 页面,每个进程(平均)会使用一个 PML4、一个 PDPT 和一个 PD;即总共约 12 KiB 的分页结构。

这给我们一些粗略的对比数据。对于 50 个进程,每个进程平均使用 10 MiB,且有 3 个不同的区域:

  • 4 KiB 分页的开销约为:填充 6 KiB + 分页结构 32 KiB,即每个进程约 38 KiB。
  • 2 MiB 分页的开销约为:填充 3 MiB + 分页结构 12 KiB,即每个进程约 3084 KiB。
  • 4 KiB 分页对于全部 50 个进程的总开销约为 1.86 MiB。
  • 2 MiB 分页对于全部 50 个进程的总开销约为 150.6 MiB。

若所有进程实际使用 500 MiB RAM:

  • 总开销 1.86 MiB 相当于多消耗约 0.37%,几乎可以忽略。
  • 总开销 150.6 MiB 相当于多消耗约 23.15%,非常可观。

对于性能(例如 TLB 未命中),很难估计可能的成本,因为这取决于分页结构有多少保留在缓存中(以及有多少必须从 RAM 中取回)、TLB 的大小(小页与大页的 TLB 都算)、CPU 是否缓存更高层级的结构(现代 CPU 会这样做)、每个进程的工作集和访问模式、进程切换频率等……

然而(对于典型的操作系统和典型负载),我会假设使用 2 MiB 页带来的性能收益非常不可能值得为此浪费大约 23% 的额外 RAM。

基本上,4 KiB 页(带四级分页结构)逐渐显得稍微有些小,但下一步跳到 2 MiB 页(带三级分页结构)在大多数情况下又太大,不太现实。

为了减少分页层级,更好的方案是同时增大页目录、PDPT 等结构的大小。例如,对于 55 位虚拟地址,你可以使用 64 KiB 页、64 KiB 页表、64 KiB 页目录和 64 KiB PDPT。然而,我们需要等待 Intel(或 AMD)做出类似的设计。

~ Brendan

幸运的是,你可以自由混合使用 4 KiB、2 MiB 和 1 GiB 页。你无需对所有内容使用统一的页大小。因此,一个拥有 9MB 数据的进程可以使用 4 个 2 MiB 页,剩余部分再用 4 KiB 页补齐。这节省了分页结构、改善了 TLB 利用率,同时不会增加额外开销。


递归映射(Recursive mapping)

为了更方便地修改当前地址空间的页映射(page map),你可以把最高级页映射层级的一个条目映射到它自身。

递归映射会浪费一些虚拟地址空间。下表显示了递归映射页表所使用的相对空间:

ModePage sizeMax page map sizeUsed virtual spaceTotal virtual spaceRatio
Protected mode (non-PAE)4 KiB4 MiB4 MiB4 GiB1/1024 (0.1%)
4 MiB4 KiB
Protected mode (PAE)4 KiB8 MiB1 GiB4 GiB1/4 (25%)
2 MiB16 KiB
Long mode (48-bit)4 KiB512 GiB512 GiB256 TiB1/512 (0.2%)
2 MiB1 GiB
1 GiB2 MiB

下表中的 “Recursive mapping” 列展示了当页映射被作为最后一项递归映射时,用于访问特定页映射层级的基地址与偏移。


Protected/compatibility mode (32-bit) page map

在保护模式下,无论是否启用 PAE,虚拟地址空间的大小都是 32 位(4 GiB)。


Non-PAE mode

未启用 PAE 时,页映射最多可引用 32 位(4 GiB)的物理页地址。

4 KiB pages

LevelTableSizeRangeBitsEntriesPagesRecursive mapping
0(page)-0x1000 (4 KiB)12 bits-0x1 (1)-
1PT0x1000 (4 KiB)0x40 0000 (4 MiB)10 bits10240x400 (1024)0xFFC0 0000 + 0x1000 * PDi
2PD0x1000 (4 KiB)0x10000 0000 (4 GiB)10 bits10240x10 0000 (1048576)0xFFFF F000

4 MiB pages

LevelTableSizeRangeBitsEntriesPagesRecursive mapping
0(page)-0x40 0000 (4 MiB)22 bits-0x1 (1)-
2PD0x1000 (4 KiB)0x10000 0000 (4 GiB)10 bits10240x400 (1024)0xFFC0 0000

PAE mode

启用 PAE 后,页映射最多可引用 36 位(64 GiB)的物理页地址。

4 KiB pages

LevelTableSizeRangeBitsEntriesPagesRecursive mapping
0(page)-0x1000 (4 KiB)12 bits-0x1 (1)-
1PT0x1000 (4 KiB)0x20 0000 (2 MiB)9 bits5120x200 (512)0xC000 0000 + 0x20 0000 * PDi + 0x1000 * PTi
2PD0x1000 (4 KiB)0x4000 0000 (1 GiB)9 bits5120x40000 (262144)0xC060 0000 + 0x1000 * PDi
3PDP0x20 (32 bytes)0x10000 0000 (4 GiB)2 bits40x10 0000 (1048576)0xC060 3000

2 MiB pages

LevelTableSizeRangeBitsEntriesPagesRecursive mapping
0(page)-0x20 0000 (2 MiB)21 bits-0x1 (1)-
2PD0x1000 (4 KiB)0x4000 0000 (1 GiB)9 bits5120x200 (512)0xC000 0000 + 0x20 0000 * PDi
3PDP0x20 (32 bytes)0x10000 0000 (4 GiB)2 bits40x800 (2048)0xC060 0000

Long mode (64-bit) page map

在长模式中,虚拟地址空间理论上可以是 64 位(16 EiB),但实际处理器只允许访问其中的一部分。目前最常见的处理器实现允许 48 位(256 TiB)的虚拟地址空间。对于这种虚拟地址,位 48–63 必须是位 47 的复制(类似符号扩展),这会将虚拟地址空间分为上半部分和下半部分。


48-bit virtual address space

4 KiB pages

LevelTableSizeRangeBitsEntriesPagesRecursive mapping
0(page)-0x1000 (4 KiB)12 bits-0x1 (1)-
1PT0x1000 (4 KiB)0x20 0000 (2 MiB)9 bits5120x200 (512)0xFFFF FF80 0000 0000 + 0x4000 0000 * PDPi + 0x20 0000 * PDi + 0x1000 * PTi
2PD0x1000 (4 KiB)0x4000 0000 (1 GiB)9 bits5120x40000 (262144)0xFFFF FFFF C000 0000 + 0x20 0000 * PDPi + 0x1000 * PDi
3PDP0x1000 (4 KiB)0x80 0000 0000 (512 GiB)9 bits5120x800 0000 (134217728)0xFFFF FFFF FFE0 0000 + 0x1000 * PDPi
4PML40x1000 (4 KiB)0x10000 0000 0000 (256 TiB)9 bits5120x10 0000 0000 (68719476736)0xFFFF FFFF FFFF F000

2 MiB pages

LevelTableSizeRangeBitsEntriesPagesRecursive mapping
0(page)-0x20 0000 (2 MiB)21 bits-0x1 (1)-
2PD0x1000 (4 KiB)0x4000 0000 (1 GiB)9 bits5120x200 (512)0xFFFF FF80 0000 0000 + 0x4000 0000 * PDPi + 0x20 0000 * PDi
3PDP0x1000 (4 KiB)0x80 0000 0000 (512 GiB)9 bits5120x40000 (262144)0xFFFF FFFF C000 0000 + 0x20 0000 * PDPi
4PML40x1000 (4 KiB)0x10000 0000 0000 (256 TiB)9 bits5120x8000000 (134217728)0xFFFF FFFF FFE0 0000

1 GiB pages

LevelTableSizeRangeBitsEntriesPagesRecursive mapping
0(page)-0x4000 0000 (1 GiB)30 bits-0x1 (1)-
3PDP0x1000 (4 KiB)0x80 0000 0000 (512 GiB)9 bits5120x200 (512)0xFFFF FF80 0000 0000 + 0x4000 0000 * PDPi
4PML40x1000 (4 KiB)0x10000 0000 0000 (256 TiB)9 bits5120x40 000 (262144)0xFFFF FFFF C000 0000

另请参见

文章

论坛