硬件分页机制原理

硬件分页机制原理

概述

分页(Paging)是 CPU 内存管理单元(MMU)提供的一种地址转换机制,将程序使用的虚拟地址(Virtual Address)转换为物理内存中的物理地址(Physical Address)。分页机制是所有现代操作系统实现虚拟内存、进程隔离、页面换出等特性的硬件基础。

本文涵盖 x86 32-bit(非PAE)、x86 PAE(物理地址扩展)、x86-64(4 级页表)和 ARM 四种分页模式,并以 ReactOS 源码中的硬件定义为例。


1. 基础概念

1.1 页面(Page)

物理内存被划分为固定大小的块,称为物理页帧(Page Frame)。x86/x64 架构的标准页面大小为4KB(0x1000 字节),此外还支持大页面(2MB、4MB、1GB)。

#definePAGE_SIZE0x1000// 4KB#definePAGE_SHIFT12// 页内偏移的位数#definePAGE_MASK0xFFFFF000// 页基址掩码(清零低12位)

1.2 页表(Page Table)

页表是存储在内存中的层级数据结构,MMU 通过遍历页表树将虚拟地址转换为物理地址。每个页表项(PTE/PDE)通常包含:

  • 物理页帧号(PFN / Page Frame Number)
  • 权限位:可读/可写/可执行/用户模式
  • 状态位:有效(Present)/已访问(Accessed)/已修改(Dirty)
  • 缓存属性:可缓存/禁止缓存/写合并/写透

1.3 控制寄存器

寄存器功能
CR0PG 位(位31)启用分页;PE 位(位0)启用保护模式
CR2存储最近一次缺页异常的虚拟地址
CR3页目录基址寄存器(PDBR),指向顶级的页表结构
CR4PAE 位(位5)启用物理地址扩展;PSE 位(位4)启用大页面;PGE 位(位7)启用全局页面

2. x86 32-bit 分页(非 PAE)

2.1 寻址模式

虚拟地址为 32 位,物理地址为 32 位(最大 4GB),两级页表。

31 22 21 12 11 0 ┌────────────┬────────────┬─────────────────┐ │ Directory │ Table │ Offset │ │ 10 bits │ 10 bits │ 12 bits │ └──────────────┴──────────────┴─────────────────┘
  • 顶级:页目录(Page Directory),1024 个 PDE,每个 32 位(4 字节),共 4KB
  • 次级:页表(Page Table),1024 个 PTE,每个 32 位(4 字节),共 4KB
  • 可寻址页数:1024 × 1024 = 1,048,576 个页(4GB)

2.2 地址转换流程

CR3 ──→ Page Directory (4KB) │ │ Directory[31:22](10 位索引) ▼ PDE(32位)──→ Page Table (4KB) │ │ Table[21:12](10 位索引) ▼ PTE(32位)──→ Physical Page (4KB) │ │ Offset[11:0](12 位) ▼ 物理地址

2.3 PDE/PTE 结构(32位)

ReactOS 定义(ntoskrnl/mm/i386/page.c):

// x86 32位 PTE 模板#definePTE_VALID0x1// 有效位(Present),位0#definePTE_READWRITE0x2// 可写(R/W),位1#definePTE_USER0x4// 用户模式(U/S),位2#definePTE_WRITETHROUGH0x8// 写透(PWT),位3#definePTE_DISABLE_CACHE0x10// 禁止缓存(PCD),位4#definePTE_ACCESSED0x20// 已访问(A),位5#definePTE_DIRTY0x40// 已修改(D),位6#definePTE_LARGEPAGE0x80// 大页面(PS),位7(仅在 PDE 中有效)#definePTE_GLOBAL0x100// 全局页面(G),位8#definePTE_PROTOTYPE0x400// 原型 PTE(软件定义),位10#definePTE_PROTECT_MASK0x612// 保护掩码(位1,4,9)// 32位 PTE 完整结构// 位 0: Present — 页面在物理内存中// 位 1: R/W — 0=只读, 1=读写// 位 2: U/S — 0=超级用户, 1=用户// 位 3: PWT — 写透缓存策略// 位 4: PCD — 禁止缓存// 位 5: A — MMU自动置位(读/写时)// 位 6: D — MMU自动置位(写时)// 位 7: PS — PDE中:0=4KB页面, 1=4MB页面(需 CR4.PSE=1)// 位 8: G — 全局页面(TLB 不刷新)// 位 9-11: Available — 操作系统可用// 位 12-31: PFN — 物理页帧号(20位,最大寻址 4GB)//// 4MB 大页面(CR4.PSE=1, PDE.PS=1):// 位 12-31: PFN(20位,实际可寻址 4MB,22位页帧号+10位片内偏移)

ReactOS 中的保护掩码映射表:

externconstULONG_PTR MmProtectToPteMask[32];// 将 MM_* 保护常量转换为硬件 PTE 标志// 例如:MM_READWRITE→设置 PTE_READWRITE,// MM_NOCACHE→设置 PTE_DISABLE_CACHE,// MM_EXECUTE→检查 NX 等externconstULONG MmProtectToValue[32];// 将 MM_* 保护常量转换为 NT API PAGE_* 常量// 例如:MM_READONLY → PAGE_READONLY,// MM_READWRITE → PAGE_READWRITE

3. x86 PAE 分页(物理地址扩展)

3.1 概述

PAE(Physical Address Extension)将物理地址从 32 位扩展到 36 位,使 x86 32 位系统最多可寻址64GB物理内存。虚拟地址仍为 32 位(进程地址空间仍为 4GB)。

启用方式:设置 CR4.PAE = 1(CR4 寄存器第 5 位)

3.2 寻址模式

PAE 将页表从 2 级扩展到3 级

31 30 29 21 20 12 11 0 ┌────────┬──────────┬──────────┬─────────────────┐ │ PDPT │ Directory│ Table │ Offset │ │ 2 bits │ 9 bits │ 9 bits │ 12 bits │ └────────┴──────────┴──────────┴─────────────────┘

每一级的 PTE/PDE 从 32 位扩大为64 位(8 字节)。

3.3 三级页表结构

级别名称条目数条目大小总大小
1页目录指针表(PDPT)48 字节32 字节(CR3 指向)
2页目录(PD)5128 字节4KB
3页表(PT)5128 字节4KB

转换流程

CR3 ──→ PDPT(4个条目,在 CR3 寄存器内部缓存) │ │ PDPT[31:30](2位索引,选择4个中的一个) ▼ PDPTE ← 64位,指向 PD │ │ PD[29:21](9位索引) ▼ PDE(64位)──→ PT 或 2MB 页面 │ │ PT[20:12](9位索引) ▼ PTE(64位)──→ 4KB 物理页面

大页面:PDE.PS = 1 时,PDE 直接指向 2MB 物理页面,跳过页表级。

ReactOS 中的 PAE 定义(ntoskrnl/mm/i386/pagepae.c):

// PAE 专用常量#definePA_PRESENT0x001#definePA_READWRITE0x002#definePA_USER0x004#definePA_WRITETHROUGH0x008#definePA_CACHE_DISABLE0x010#definePA_ACCESSED0x020#definePA_DIRTY0x040#definePA_LARGEPAGE0x080// 2MB 页面#definePA_GLOBAL0x100#definePA_PROTOTYPE0x400// 软件定义#definePA_NX0x8000000000000000ULL// 第63位,不可执行// PAE PDE/PTE 结构(64位)// 位 0: Present// 位 1: R/W// 位 2: U/S// 位 3-4: PWT, PCD// 位 5: Accessed// 位 6: Dirty// 位 7: PS — PDE中:0→指向 PT, 1→2MB 页面// 位 8: Global// 位 9-11: Available// 位 12-35: PFN(24位 → 36位物理地址,寻址 64GB)// 低 12 位补零得到 36 位物理地址// 位 36-51: Reserved(须为0)// 位 52-62: Available// 位 63: NX — 不可执行// PAE 页目录指针表(PDPT)结构// CR3 指向 PDPT,每个 PDPTE 指向一个页目录(PD)#definePAE_PDPT_ENTRIES4#definePAE_PAGE_TABLES4// 4个页目录,每个对应 1GB 地址空间

3.4 与 x86-64 的关系

x86-64(长模式)启动时必须先启用 PAE。长模式下的页表是 PAE 的直接扩展,在 PDPT 之上增加了 PML4 级。


4. x86-64 分页(4 级页表 / 长模式)

4.1 寻址模式

虚拟地址为64 位,但当前处理器只使用低48 位(即 256TB 地址空间)。高 16 位必须为符号扩展(与位 47 相同)。物理地址可达52 位(当前典型实现为 40 或 48 位)。

4.2 四级页表结构

47 39 38 30 29 21 20 12 11 0 ┌──────────┬──────────┬──────────┬──────────┬─────────────────┐ │ PML4 │ PDPT │ PD │ PT │ Offset │ │ 9 bits │ 9 bits │ 9 bits │ 9 bits │ 12 bits │ └──────────┴──────────┴──────────┴──────────┴─────────────────┘
级别名称条目数条目大小总大小
1PML4(Page Map Level 4)5128 字节4KB
2PDPT(Page Directory Pointer Table)5128 字节4KB
3PD(Page Directory)5128 字节4KB
4PT(Page Table)5128 字节4KB

每个页表项 64 位,每个页表 512 项 × 8 字节 = 4KB(整体对齐)。

4 级页表的可寻址范围

  • 4KB 页面:512⁴ = 2³⁶ 个页,覆盖 2³⁶ × 4KB = 256TB
  • 2MB 页面(跳过 PT 级):512³ = 2²⁷ 个页,覆盖 512 GB
  • 1GB 页面(跳过 PT 和 PD 级):512² = 2¹⁸ 个页,覆盖 512 TB

4.3 地址转换流程

CR3 ──→ PML4 Table(4KB) │ │ PML4[47:39](9位索引) ▼ PML4E(64位)──→ PDPT(4KB) │ │ PDPT[38:30](9位索引) ▼ PDPTE(64位)──→ PD(4KB) │ │ PD[29:21](9位索引) ▼ PDE(64位) │ ┌────────────┴────────────┐ │ PS=0 │ PS=1 ▼ ▼ PT(4KB) 2MB Physical Page │ │ PT[20:12](9位索引) ▼ PTE(64位)──→ 4KB Physical Page

4.4 PML4E/PDPTE/PDE/PTE 结构(64位)

来源:Intel Manual Vol 3,ReactOS mmtypes.h(sdk/include/ndk/amd64/mmtypes.h

// 64位页表项结构(PML4E/PDPTE/PDE/PTE) // 位 0: Present (P) // 位 1: R/W // 位 2: U/S // 位 3: PWT (Write-Through) // 位 4: PCD (Cache Disable) // 位 5: Accessed (A) — MMU 置位 // 位 6: Dirty (D) — MMU 置位(PDE中2MB页面有效) // 位 7: PS (Page Size) — PML4E=0; PDPTE: 1→1GB页; PDE: 1→2MB页 // 位 8: Global (G) — 仅在 PDE/PTE 中有效 // 位 9-11: Available — 操作系统可用(如 ReactOS 用位10作原型PTE标志) // 位 12-51: PFN — 物理页帧号(最多 52-12=40位,支持 1TB 物理内存) // 位 52-62: Available — 操作系统可用 // 位 63: NX (No-Execute) — 不可执行(XD 位) // 4KB 页面的 PTE 格式 // 位 12-51: PFN(40位,物理地址低12补零 = 最大52位物理地址)

ReactOS amd64 定义(sdk/include/ndk/amd64/mmtypes.h):

typedefstruct_MMPTE{union{ULONG_PTR Long;// 64位完整值HARDWARE_PTE Flush;// TLB 刷新用的硬件结构MMPTE_HARDWARE Hard;// 硬件 PTE 解释MMPTE_PROTOTYPE Proto;// 原型 PTE(软件定义)MMPTE_SOFTWARE Soft;// 软件 PTE(请求零页等)MMPTE_TRANSITION Trans;// 转换态 PTE(页面在备用/修改列表)MMPTE_SUBSECTION Subsect;// 子段 PTE(Section 映射)MMPTE_LIST List;// 列表 PTE(页表项链表)}u;}MMPTE,*PMMPTE,MMPDE,*PMMPDE,// 页目录项MMPPE,*PMMPPE,// 页目录指针表项MMPXE,*PMMPXE;// PML4 项// x86-64 PTE 标志(ReactOS miarm.h)#definePTE_READONLY0x8000000000000000ULL// NX 位#definePTE_EXECUTE0x0000000000000000ULL#definePTE_EXECUTE_READPTE_EXECUTE// x64 上 EXECUTE 隐含 READ#definePTE_READWRITE0x8000000000000002ULL// NX+R/W#definePTE_WRITECOPY0x8000000000000200ULL#definePTE_EXECUTE_READWRITE0x0000000000000002ULL#definePTE_EXECUTE_WRITECOPY0x0000000000000200ULL#definePTE_PROTOTYPE0x0000000000000400ULL#definePTE_VALID0x0000000000000001ULL#definePTE_ACCESSED0x0000000000000020ULL#definePTE_DIRTY0x0000000000000040ULL#definePTE_DISABLE_CACHE0x0000000000000010ULL#definePTE_WRITECOMBINED_CACHE0x0000000000000010ULL#definePTE_PROTECT_MASK0x8000000000000612ULL// 保护相关位掩码

4.5 大页面模式

页面大小所需页表级虚拟地址拆分
4KBPML4 → PDPT → PD → PT(4级)9+9+9+9+12 = 48位
2MBPML4 → PDPT → PD(3级)9+9+9+21 = 48位
1GBPML4 → PDPT(2级)9+9+30 = 48位

5. ARM 分页

5.1 二级页表

ARMv7-A 架构使用二级页表:

31 20 19 12 11 0 ┌──────────┬──────────┬─────────────────┐ │ PDE │ PTE │ Offset │ │ 12 bits │ 8 bits │ 12 bits │ └──────────┴──────────┴─────────────────┘
级别名称条目数条目大小说明
1页目录(Page Directory)40964 字节覆盖 1MB/4KB 页面
2页表(Page Table)2564 字节仅在 4KB 页面时使用

页面大小选项(ARMv7):

  • 节(Section):1MB,一级页表直接映射(PDE.位1=0、位0=1)
  • 粗页(Large Page):64KB,二级页表(16个4KB子页)
  • 小页(Small Page):4KB,二级页表

ReactOS ARM 定义(sdk/include/ndk/arm/mmtypes.h):

typedefstruct_MMPTE{union{ULONG_PTR Long;HARDWARE_PTE Flush;MMPTE_HARDWARE Hard;MMPTE_PROTOTYPE Proto;MMPTE_SOFTWARE Soft;MMPTE_TRANSITION Trans;MMPTE_SUBSECTION Subsect;MMPTE_LIST List;}u;}MMPTE,*PMMPTE;// ARM 页目录项typedefstruct_MMPDE{union{MMPDE_HARDWARE Hard;ULONG Long;}u;}MMPDE,*PMMPDE;// ARM PDE 硬件结构typedefunion_MMPDE_HARDWARE{ULONG Valid:1;ULONG LargePage:1;ULONG Buffered:1;ULONG Cached:1;ULONG NoExecute:1;ULONG Domain:4;ULONG Ecc:1;ULONG PageFrameNumber:22;}MMPDE_HARDWARE,*PMMPDE_HARDWARE;// ARM PTE 标志(ReactOS miarm.h)#definePTE_READONLY0x200#definePTE_PROTOTYPE0x400// 使用共享位

注意:ReactOS 的 ARM 分支中大部分页表操作为UNIMPLEMENTED存根,ARM 架构的支持仍在开发中。


6. TLB 与缓存管理

6.1 转换后备缓冲区(TLB)

TLB(Translation Lookaside Buffer)是 MMU 内部的页表缓存,存储最近使用的虚拟地址到物理地址的转换。每次内存访问时 CPU 先查 TLB,命中则直接获得物理地址;未命中才遍历页表(page walk)。

TLB 刷新

// x86 单 CPU TLB 刷新 — 使用 invlpg 指令使单个页面失效__invlpg(VirtualAddress);// x86 完整 TLB 刷新 — 重新加载 CR3 寄存器// (CR3 写入操作会刷新所有非全局 TLB 条目)asmvolatile("mov %0, %%cr3"::"r"(NewCr3Value):"memory");// 全局页面 — 设置 PTE_GLOBAL=1,TLB 刷新时不刷新这些条目#definePTE_GLOBAL0x100

ReactOS PAE 模式的 TLB 刷新(ntoskrnl/mm/i386/pagepae.c):

// 单处理器:直接 invlpgMiFlushTlb(LinearAddress){__invlpg(LinearAddress);}// 多处理器:通过 IPI 在所有 CPU 上执行刷新MiFlushTlbIpiRoutine(VOID){// 在 IPI 中断处理器中执行 TLB 刷新__invlpg(Thread->TlbFlushAddress);}

6.2 页表结构缓存

除了 TLB,处理器还缓存页表项本身(Paging-Structure Caches),包括 PDE、PDPTE、PML4E 的缓存。当操作系统修改页表时,必须使用invlpg或 CR3 切换使这些缓存失效。

6.3 缓存属性位

含义缓存策略
PCD=0, PWT=0Write-Back(回写)默认策略,性能最高
PCD=0, PWT=1Write-Through(写透)写入同时更新内存
PCD=1, PWT=0Uncached(非缓存)用于 MMIO 设备寄存器
PCD=1, PWT=1Uncached(非缓存)同上

7. 分页模式对比

特性32-bit(非PAE)PAEx86-64(长模式)
虚拟地址宽度32 位32 位48 位(扩展到 64)
物理地址宽度32 位36 位52 位
最大物理内存4 GB64 GB4 PB
页表级数234
每级索引位数10+102+9+99+9+9+9
标准页面大小4KB4KB4KB
大页面大小4MB2MB2MB(第3级) / 1GB(第2级)
PTE/PDE 大小32 位64 位64 位
每页表条目数1024512512
NX 支持是(位63)是(位63)
CR3 指向页目录页目录指针表PML4 表

各架构的分页模式映射

非PAE (2级): PAE (3级): x86-64 (4级): CR3 CR3 CR3 │ │ │ ▼ ▼ ▼ ┌──────┐ ┌──────┐ ┌──────┐ │ PD │ │ PDPT │ │ PML4 │ │1024个│ │ 4个 │ │512个 │ └──┬───┘ └──┬───┘ └──┬───┘ │ │ │ ▼ ▼ ▼ ┌──────┐ ┌──────┐ ┌──────┐ │ PT │ │ PD │ │ PDPT │ │1024个│ │512个 │ │512个 │ └──┬───┘ └──┬───┘ └──┬───┘ │ │ │ ▼ ▼ ▼ ┌──────┐ ┌──────┐ ┌──────┐ │ 4KB │ │ PT │ │ PD │ │ Page │ │512个 │ │512个 │ └──────┘ └──┬───┘ └──┬───┘ │ │ ▼ ▼ ┌──────┐ ┌──────┐ │ 4KB │ │ PT │ │ Page │ │512个 │ └──────┘ └──┬───┘ │ ▼ ┌──────┐ │ 4KB │ │ Page │ └──────┘

8. ReactOS 中的页表自映射技巧

x86 架构下,ReactOS 利用页目录的最后一项指向页目录自身,实现页表的虚拟可寻址性。这使得内核可以通过普通虚拟地址直接访问任意 PTE,无需物理地址转换或临时映射。

PD[1023] → PD 自身的物理地址 ← 自引用 PD[1022] → 超空间 PDE ← 临时物理页映射 PD[0..1021] → 进程页表项 ← 正常映射 通过 PTE_BASE 访问任意 PTE: PTE(vaddr) = (PMMPTE)(PTE_BASE + (vaddr >> 12) * sizeof(MMPTE)) → 即 PTE_BASE[index] 对应虚拟地址为 index<<12 的页表项
// ReactOS 中的自映射区域常量(miarm.h)#definePTE_BASE((PMMPTE)0xC0000000)// x86 页表自映射基址#definePDE_BASE((PMMPDE)0xC0300000)// x86 页目录自映射基址// 宏:虚拟地址 → PTE 地址#defineMiAddressToPte(Va)\((PMMPTE)(((ULONG_PTR)(Va)>>12)*sizeof(MMPTE)+(ULONG_PTR)PTE_BASE))// 宏:验证地址是否在页表区域#defineMI_IS_PAGE_TABLE_ADDRESS(Address)\(((PVOID)(Address)>=(PVOID)PTE_BASE)&&((PVOID)(Address)<=(PVOID)PTE_TOP))

amd64 下通过 PML4 自映射实现类似效果(PML4E[511]指向 PML4 自身)。


9. 关键技术总结

  1. 页表自映射:x86/x64 利用顶级页表的一个自引用条目,使页表本身在虚拟地址空间中可寻址,操作系统无需在多个页表间切换即可修改任意 PTE。

  2. TLB 刷新策略:切换进程时写入 CR3(或 C111—ASID),全局页面(PTE_GLOBAL)不受 TLB 刷新的影响,通常映射内核页面。

  3. NX 位:PAE 模式引入了 NX 位(位63),用于标记不可执行的内存页。x86-64 长模式强制使用 NX 位,这是硬件 DEP(数据执行保护)的基础。

  4. 大页面:PDE.PS=1 时,PDE 直接映射大页面(4MB/2MB/1GB),减少 TLB 缺失。ReactOS 通过largepag.c管理大页面分配。

  5. PDE/PTE 模板:ReactOS 在初始化时预定义ValidKernelPteDemandZeroPte等模板,页面分配时直接复用,避免重复计算硬件标志。

  6. 软件位:PTE 的位 9-11(位 52-62 在 x64)可供操作系统自由使用。ReactOS 使用位 10(PTE_PROTOTYPE)标记原型 PTE,位 9 标记转换态 PTE 等。


参考资料

  1. Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A: Chapter 4 “Paging”
  2. AMD64 Architecture Programmer’s Manual, Volume 2: Chapter 5 “Page Translation and Protection”
  3. ReactOS 源码sdk/include/ndk/*/mmtypes.h(架构相关 PTE/PDE 结构)
  4. ReactOS 源码ntoskrnl/mm/ARM3/miarm.h(PTE 标志和软件位定义)
  5. ReactOS 源码ntoskrnl/mm/i386/page.c/pagepae.c(x86 页表操作实现)
  6. ReactOS 源码ntoskrnl/mm/amd64/init.c(amd64 页表初始化)