Linux内核MMU TLB缓存刷新与TLB缺失优化工程 - 第1节

1. MMU基础概念

现代操作系统(如 Linux)运行在 MMU(Memory Management Unit,内存管理单元) 之上。MMU 的核心职责是将程序看到的 虚拟地址(Virtual Address, VA) 转换为硬件实际访问的 物理地址(Physical Address, PA)。这种两层抽象是内存隔离、进程独立和高效利用物理内存的基石。

关键概念: 虚拟地址是 CPU 执行指令时发出的地址(如 0x7ffe1234),物理地址是内存芯片上真实的存储单元编号(如 0x0000abcd)。MMU 负责在两者之间建立映射关系。

1.1.1 为什么需要虚拟地址?

  • 进程隔离: 每个进程拥有独立的虚拟地址空间,不会误写其他进程的内存。
  • 简化编程: 程序员无需关心物理内存的碎片化或容量限制,可以连续使用大块地址。
  • 内存共享与保护: 通过页表权限位(读/写/执行)控制访问,内核可安全地共享代码段或内存映射。
  • 按需分配: 物理内存仅在真正访问时才分配(缺页异常),提高内存利用率。

1.1.2 地址转换概览

从 CPU 发出虚拟地址到最终访问物理内存,经过以下关键步骤:

  1. CPU 取指或访存,发出虚拟地址 VA。
  2. MMU 根据 VA 的高位索引页表,查找对应的页表项(PTE)。
  3. 如果 PTE 有效且权限允许,MMU 提取物理页帧号(PFN),拼接页内偏移得到物理地址 PA。
  4. 如果 PTE 无效或权限不足,触发缺页异常(Page Fault),由内核处理。
性能提示: 每次地址转换都查内存中的页表会非常慢(多次访存)。因此 MMU 内部集成了 TLB(Translation Lookaside Buffer)缓存最近使用的页表项,这是后续章节优化的核心。
虚拟地址到物理地址转换流程 CPU 发出 VA MMU 查页表 / TLB 权限检查 物理内存 PA 访问 缺页异常 → 内核处理

1.2 页表:地址转换的映射表

页表是存储在内存中的一种数据结构,用于将虚拟页号(VPN)映射到物理页帧号(PFN)。Linux 内核使用 多级页表(通常为 4 级或 5 级,取决于架构)来节省内存,避免为每个虚拟地址都建立一级映射。

1.2.1 多级页表结构(以 x86_64 4 级页表为例)

虚拟地址被划分为多个位段,每段索引一级页表。典型的 4 级页表包括:

  • PGD(Page Global Directory): 顶级目录,由进程的 mm_struct 指向。
  • P4D(Page 4th Directory): 第 4 级目录(某些架构或配置下存在)。
  • PUD(Page Upper Directory): 第 3 级目录。
  • PMD(Page Middle Directory): 第 2 级目录。
  • PTE(Page Table Entry): 最后一级页表,直接指向物理页帧。
注意: 不同架构(ARM64、RISC-V)的页表级数和位段划分可能不同,但核心思想一致:通过多级索引缩小页表占用的物理内存。
多级页表结构(4级) 虚拟地址位段划分 PGD索引 PUD索引 PMD索引 PTE索引 页内偏移 PGD PUD PMD PTE 物理页 每级页表存储下一级页表的物理地址或最终物理页帧号

1.3 页表项(PTE):映射的核心单元

页表项(PTE)是页表中的最小数据单元,通常为 8 字节(64 位架构)。它包含了物理页帧号(PFN)以及一系列权限和状态标志位。

1.3.1 PTE 的典型字段

字段 位宽(示例) 作用
物理页帧号(PFN) 高 52 位(x86_64) 指向物理内存中 4KB 页帧的基地址
Present(有效位) 1 位 该页是否在物理内存中(0 表示缺页或换出)
R/W(读写位) 1 位 0 表示只读,1 表示可读写
U/S(用户/超级用户位) 1 位 0 表示仅内核可访问,1 表示用户态也可访问
Dirty(脏位) 1 位 该页是否被写入过(用于回写策略)
Accessed(访问位) 1 位 该页是否被读取或写入过(用于页面置换算法)
NX(不可执行位) 1 位 禁止在该页上执行代码(硬件安全特性)
优化视角: PTE 的 Accessed 和 Dirty 位由硬件自动设置,但软件(内核)需要定期检查或清除。TLB 缺失时,MMU 必须读取完整的 PTE 才能完成地址转换,因此 PTE 的布局和缓存效率直接影响性能。

1.3.2 PTE 在地址转换中的作用

当 MMU 遍历多级页表找到最后一级 PTE 时,执行以下操作:

  1. 检查 Present 位:若为 0,触发缺页异常;若为 1,继续。
  2. 检查权限位(R/W, U/S, NX):若违反当前 CPU 模式(用户态/内核态)或操作类型(读/写/执行),触发保护错误。
  3. 提取 PFN,与虚拟地址的页内偏移拼接,形成完整的物理地址。
  4. 硬件自动设置 Accessed 位(若未设置),并根据写操作设置 Dirty 位。
页表项(PTE)结构及地址转换 PTE 位字段布局(64位) 物理页帧号(PFN) P R/W U/S D A NX … 地址转换过程 虚拟地址 VA VPN + 偏移 PTE 查找 多级页表遍历 物理地址 PA PFN + 偏移 PTE 的 Present 位为 0 时,转换失败,触发缺页异常

1.4 页表与 PTE 在 Linux 内核中的体现

Linux 内核通过 struct page 和 pte_t 类型来管理物理页和页表项。关键数据结构如下:

// Linux 内核中页表项类型(x86_64)
typedef struct { unsigned long pte; } pte_t;

// 页全局目录项
typedef struct { unsigned long pgd; } pgd_t;

// 宏:读取 PTE 的物理页帧号
#define pte_pfn(x) ((unsigned long)(((x).pte >> PAGE_SHIFT) & ((1UL << (64 - PAGE_SHIFT)) - 1)))

// 宏:设置 PTE 的权限
#define pte_mkwrite(pte)  __pte(pte_val(pte) | _PAGE_RW)
#define pte_mkdirty(pte)  __pte(pte_val(pte) | _PAGE_DIRTY)
内核优化点: 在 TLB 缺失处理路径中,内核会频繁读取和修改 PTE。例如,当页面被换出时,内核将 PTE 的 Present 位清 0,并将换出位置编码到 PTE 的剩余位中(swap entry)。这种设计避免了额外的数据结构查找,但要求 TLB 刷新操作必须同步。

1.5 本章小结

概念 核心作用 与 TLB 优化的关系
虚拟地址 进程独立的内存视图 TLB 缓存的是 VA→PA 的映射
物理地址 真实硬件访问地址 TLB 命中后直接输出 PA
多级页表 节省页表内存,支持稀疏地址空间 TLB 缺失时需遍历多级页表,增加延迟
页表项(PTE) 存储映射关系和权限/状态位 PTE 的 Present/Dirty/Accessed 位影响 TLB 刷新策略