x86_64 Linux内核MMU分页机制与CR3切换详解 - 第1节
1.1 MMU 在 x86_64 体系结构中的角色
内存管理单元(Memory Management Unit, MMU)是 x86_64 处理器中负责将虚拟地址转换为物理地址的硬件模块。它位于 CPU 核心与内存总线之间,是操作系统实现进程隔离、虚拟内存、按需分页等核心功能的基础。
核心角色:MMU 使得每个进程可以拥有独立的、连续的虚拟地址空间(0x0000000000000000 ~ 0x00007FFFFFFFFFFF 用户空间 + 0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF 内核空间),而实际物理内存可以是离散的、甚至被换出到磁盘。
MMU 在指令执行路径中的位置
当 CPU 执行一条访问内存的指令(如 mov rax, [rbx])时,地址转换流程如下:
- CPU 核心发出虚拟地址(VA)
- MMU 接收 VA,查询页表(Page Table)进行地址转换
- TLB(Translation Lookaside Buffer)作为页表的硬件缓存,加速转换
- MMU 输出物理地址(PA)到内存控制器
- 内存控制器访问物理内存
关键理解:MMU 的转换对软件(包括操作系统内核)是透明的。内核只需维护正确的页表结构,MMU 硬件自动完成每次地址查找。CR3 寄存器指向当前进程的顶级页表基址。
1.2 虚拟地址到物理地址的映射原理
x86_64 使用 4 级分页(在某些新处理器上支持 5 级分页)将 48 位虚拟地址转换为物理地址。映射的基本单位是页(Page),通常大小为 4KB。虚拟地址空间被划分为固定大小的页,物理内存也被划分为相同大小的页框(Page Frame)。
地址转换的核心公式
| 组件 | 说明 | 位数(4KB 页) |
|---|---|---|
| 虚拟地址 | 程序使用的地址 | 48 位(有效) |
| 页内偏移 | 页内的字节位置 | 12 位(0-4095) |
| 虚拟页号(VPN) | 虚拟地址的高 36 位,用于索引页表 | 36 位 |
| 物理页框号(PFN) | 物理内存中页框的编号 | 取决于物理内存大小 |
| 物理地址 | 实际访问内存的地址 = PFN + 偏移 | 最多 52 位 |
注意:虚拟地址的高 16 位(位 47 以上)必须是对位 47 的符号扩展。如果位 47 为 0,则高 16 位必须全为 0(用户空间);如果位 47 为 1,则高 16 位必须全为 1(内核空间)。否则 MMU 会触发一般保护异常(#GP)。
1.3 分页机制的基本概念
分页(Paging)是 x86_64 体系结构默认且唯一支持的内存管理方式(已移除分段机制的传统模式)。它将虚拟内存和物理内存都划分为固定大小的块,通过多级页表实现地址转换。
4 级分页结构
x86_64 使用 4 级页表层次结构,每一级都是一个页表(Page Table),包含若干页表项(Page Table Entry, PTE)。各级页表名称如下:
| 级别 | 名称 | 缩写 | 每项索引位数 | 每项大小 |
|---|---|---|---|---|
| L4 | Page Map Level 4 Table | PML4 | 9 位 | 8 字节 |
| L3 | Page Directory Pointer Table | PDPT | 9 位 | 8 字节 |
| L2 | Page Directory Table | PDT | 9 位 | 8 字节 |
| L1 | Page Table | PT | 9 位 | 8 字节 |
虚拟地址的 48 位被划分为:9 位(PML4 索引) + 9 位(PDPT 索引) + 9 位(PDT 索引) + 9 位(PT 索引) + 12 位(页内偏移)。
页表项(PTE)结构
每个页表项为 8 字节(64 位),包含物理地址高位和标志位。关键标志位如下:
| 位域 | 名称 | 说明 |
|---|---|---|
| 位 0 | P(Present) | 页是否在物理内存中 |
| 位 1 | R/W(Read/Write) | 0=只读,1=读写 |
| 位 2 | U/S(User/Supervisor) | 0=内核态,1=用户态可访问 |
| 位 3 | PWT(Page Write Through) | 缓存写策略 |
| 位 4 | PCD(Page Cache Disable) | 禁止缓存 |
| 位 5 | A(Accessed) | 页是否被访问过(由硬件置位) |
| 位 6 | D(Dirty) | 页是否被写入过(由硬件置位) |
| 位 7 | PS(Page Size) | 仅在 PDPT/PDT 中有效,1=大页(2MB/1GB) |
| 位 8 | G(Global) | 全局页,TLB 刷新时不被清除 |
| 位 12-51 | 物理地址高位 | 页框物理地址的高 40 位(4KB 页对齐) |
| 位 52-62 | 软件可用位 | 操作系统可自定义使用 |
| 位 63 | NX(No Execute) | 1=禁止执行该页代码 |
Linux 内核视角:Linux 将页表项中的软件可用位(位 52-62)用于存储页状态标志,如
_PAGE_SOFT_DIRTY、_PAGE_SPECIAL 等。NX 位是实现 NX(No-Execute)保护的关键,防止代码在数据页上执行。
分页机制的关键特性
- 进程隔离:每个进程拥有独立的页表层次结构,CR3 在进程切换时更新,确保进程间地址空间互不干扰。
- 按需分页:页表项中的 Present 位为 0 时,访问该页会触发缺页异常(Page Fault),内核可据此从磁盘加载数据。
- 大页支持:通过设置 PS 位,可以使用 2MB(在 PDT 级)或 1GB(在 PDPT 级)的大页,减少 TLB 压力。
- 写时复制(COW):fork() 后父子进程共享只读页,写操作触发缺页异常,内核复制物理页。
- TLB 刷新:修改页表后,必须刷新 TLB(通过
invlpg指令或重新加载 CR3),否则 MMU 可能使用过时的缓存。
总结:MMU 是 x86_64 虚拟内存系统的硬件引擎,通过 4 级页表结构将 48 位虚拟地址转换为物理地址。分页机制提供了进程隔离、按需加载、大页优化等关键能力,是 Linux 内核内存管理的基石。CR3 寄存器作为页表层次结构的根指针,在进程上下文切换时被内核修改,从而实现地址空间的切换。