硬件分页机制原理
概述
分页(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 控制寄存器
| 寄存器 | 功能 |
|---|---|
| CR0 | PG 位(位31)启用分页;PE 位(位0)启用保护模式 |
| CR2 | 存储最近一次缺页异常的虚拟地址 |
| CR3 | 页目录基址寄存器(PDBR),指向顶级的页表结构 |
| CR4 | PAE 位(位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_READWRITE3. 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) | 4 | 8 字节 | 32 字节(CR3 指向) |
| 2 | 页目录(PD) | 512 | 8 字节 | 4KB |
| 3 | 页表(PT) | 512 | 8 字节 | 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 │ └──────────┴──────────┴──────────┴──────────┴─────────────────┘| 级别 | 名称 | 条目数 | 条目大小 | 总大小 |
|---|---|---|---|---|
| 1 | PML4(Page Map Level 4) | 512 | 8 字节 | 4KB |
| 2 | PDPT(Page Directory Pointer Table) | 512 | 8 字节 | 4KB |
| 3 | PD(Page Directory) | 512 | 8 字节 | 4KB |
| 4 | PT(Page Table) | 512 | 8 字节 | 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 Page4.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 大页面模式
| 页面大小 | 所需页表级 | 虚拟地址拆分 |
|---|---|---|
| 4KB | PML4 → PDPT → PD → PT(4级) | 9+9+9+9+12 = 48位 |
| 2MB | PML4 → PDPT → PD(3级) | 9+9+9+21 = 48位 |
| 1GB | PML4 → 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) | 4096 | 4 字节 | 覆盖 1MB/4KB 页面 |
| 2 | 页表(Page Table) | 256 | 4 字节 | 仅在 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_GLOBAL0x100ReactOS 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=0 | Write-Back(回写) | 默认策略,性能最高 |
| PCD=0, PWT=1 | Write-Through(写透) | 写入同时更新内存 |
| PCD=1, PWT=0 | Uncached(非缓存) | 用于 MMIO 设备寄存器 |
| PCD=1, PWT=1 | Uncached(非缓存) | 同上 |
7. 分页模式对比
| 特性 | 32-bit(非PAE) | PAE | x86-64(长模式) |
|---|---|---|---|
| 虚拟地址宽度 | 32 位 | 32 位 | 48 位(扩展到 64) |
| 物理地址宽度 | 32 位 | 36 位 | 52 位 |
| 最大物理内存 | 4 GB | 64 GB | 4 PB |
| 页表级数 | 2 | 3 | 4 |
| 每级索引位数 | 10+10 | 2+9+9 | 9+9+9+9 |
| 标准页面大小 | 4KB | 4KB | 4KB |
| 大页面大小 | 4MB | 2MB | 2MB(第3级) / 1GB(第2级) |
| PTE/PDE 大小 | 32 位 | 64 位 | 64 位 |
| 每页表条目数 | 1024 | 512 | 512 |
| 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. 关键技术总结
页表自映射:x86/x64 利用顶级页表的一个自引用条目,使页表本身在虚拟地址空间中可寻址,操作系统无需在多个页表间切换即可修改任意 PTE。
TLB 刷新策略:切换进程时写入 CR3(或 C111—ASID),全局页面(
PTE_GLOBAL)不受 TLB 刷新的影响,通常映射内核页面。NX 位:PAE 模式引入了 NX 位(位63),用于标记不可执行的内存页。x86-64 长模式强制使用 NX 位,这是硬件 DEP(数据执行保护)的基础。
大页面:PDE.PS=1 时,PDE 直接映射大页面(4MB/2MB/1GB),减少 TLB 缺失。ReactOS 通过
largepag.c管理大页面分配。PDE/PTE 模板:ReactOS 在初始化时预定义
ValidKernelPte、DemandZeroPte等模板,页面分配时直接复用,避免重复计算硬件标志。软件位:PTE 的位 9-11(位 52-62 在 x64)可供操作系统自由使用。ReactOS 使用位 10(
PTE_PROTOTYPE)标记原型 PTE,位 9 标记转换态 PTE 等。
参考资料
- Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A: Chapter 4 “Paging”
- AMD64 Architecture Programmer’s Manual, Volume 2: Chapter 5 “Page Translation and Protection”
- ReactOS 源码:
sdk/include/ndk/*/mmtypes.h(架构相关 PTE/PDE 结构) - ReactOS 源码:
ntoskrnl/mm/ARM3/miarm.h(PTE 标志和软件位定义) - ReactOS 源码:
ntoskrnl/mm/i386/page.c/pagepae.c(x86 页表操作实现) - ReactOS 源码:
ntoskrnl/mm/amd64/init.c(amd64 页表初始化)