3. 分页机制原理
分页机制是Linux内存管理的核心基石。它通过页表(Page Table)这一硬件-软件协同的数据结构,将虚拟地址空间映射到物理地址空间。本章节深入剖析页表的作用、多级页表的结构设计以及页表项中关键标志位的含义。
核心概念:页表是操作系统为每个进程维护的一张“地址转换地图”。CPU发出的虚拟地址通过页表查询,最终转换为物理地址,从而实现对物理内存的隔离、保护和高效利用。
3.1 页表的作用:虚拟地址到物理地址的桥梁
页表的核心作用可以概括为以下三点:
- 地址转换:将连续的虚拟地址空间映射到离散的物理页框,解决物理内存碎片化问题。
- 内存保护:通过页表项中的权限位(读/写/执行),实现进程间的地址空间隔离,防止越权访问。
- 虚拟内存支持:页表项中的“存在位”标识页面是否在物理内存中,若不在则触发缺页异常(Page Fault),由内核从磁盘交换区加载。
关键理解:没有页表,每个进程都能直接访问物理地址,系统将毫无安全性可言。页表是虚拟化技术的基石。
3.2 多级页表结构:PGD / PUD / PMD / PTE
现代64位系统(如x86_64)使用4级页表结构,以解决单级页表占用连续大块物理内存的问题。Linux内核抽象出通用的4级页表模型,具体硬件实现可能有所不同(如ARM64支持3级或4级)。
4级页表的层级与名称如下:
| 层级 | 缩写 | 全称 | 作用 |
|---|---|---|---|
| 第1级 | PGD | Page Global Directory | 顶级页表,每个进程一个,指向下一级目录 |
| 第2级 | PUD | Page Upper Directory | 上层页目录,进一步细分地址空间 |
| 第3级 | PMD | Page Middle Directory | 中间页目录,指向页表 |
| 第4级 | PTE | Page Table Entry | 页表项,直接指向物理页框 |
虚拟地址被CPU硬件解析为多个索引字段,依次遍历这4级页表,最终找到物理页框号(PFN)。
地址转换流程:虚拟地址 → PGD索引 → PUD索引 → PMD索引 → PTE索引 → 物理页框 + 页内偏移 = 物理地址。
下图展示了x86_64架构下4级页表的地址转换过程:
3.3 页表项(PTE)标志位详解
每个页表项(PTE)不仅包含物理页框号(PFN),还包含一组标志位(Flags),用于控制页面的访问权限、缓存策略和状态。以下是Linux内核中常用的PTE标志位:
| 标志位 | 名称 | 含义 | 典型值 |
|---|---|---|---|
| P | Present | 页面是否在物理内存中。若为0,访问该页会触发缺页异常。 | 0 / 1 |
| R/W | Read/Write | 页面是否可写。0表示只读,1表示可读写。 | 0 / 1 |
| U/S | User/Supervisor | 页面是否可在用户态访问。0表示仅内核态可访问,1表示用户态也可访问。 | 0 / 1 |
| A | Accessed | 页面是否被访问过(读或写)。由硬件自动置位,内核定期检查用于页面置换。 | 0 / 1 |
| D | Dirty | 页面是否被写入过。用于判断是否需要回写至磁盘交换区。 | 0 / 1 |
| NX | No-Execute | 页面是否禁止执行代码。用于防止缓冲区溢出攻击(DEP/NX保护)。 | 0 / 1 |
| G | Global | 页面是否为全局页(如内核空间)。全局页在TLB刷新时不会被清除,提升性能。 | 0 / 1 |
| PAT | Page Attribute Table | 页面内存类型(如回写、不可缓存、写合并等),用于控制缓存行为。 | 0 / 1 |
安全提示:NX(No-Execute)位是现代操作系统安全机制(如Linux的NX、Windows的DEP)的核心。如果攻击者试图在数据页(如栈或堆)上执行代码,CPU会因NX位为1而拒绝执行,从而阻止缓冲区溢出攻击。
3.4 多级页表的优势与TLB加速
多级页表相比单级页表具有以下显著优势:
- 节省内存:单级页表需要为整个虚拟地址空间分配连续的页表数组(例如4GB虚拟空间需要4MB连续页表)。多级页表允许按需分配,未使用的地址范围不需要分配中间页表。
- 按需创建:进程创建时,内核只分配PGD和少量必要的中间页表。随着进程使用更多内存,再逐步分配PUD、PMD和PTE。
- 共享页表:内核空间的高地址部分(如所有进程共享的内核映射)可以共享同一套页表,进一步节省内存。
为了加速页表查询,CPU内置了TLB(Translation Lookaside Buffer,转译后备缓冲器),即页表的高速缓存。TLB存储最近使用的虚拟地址到物理地址的映射。当CPU访问内存时,首先在TLB中查找,若命中则无需遍历多级页表,大幅提升性能。
性能关键:TLB命中率直接影响系统性能。多级页表虽然增加了单次查询的步骤,但通过TLB缓存,实际开销被大大降低。内核开发者会尽量优化页表布局,以提高TLB命中率。
下图展示了TLB在地址转换中的加速作用:
3.5 内核中的页表操作接口
Linux内核提供了一套宏和函数来操作页表,这些接口在arch/x86/include/asm/pgtable.h等头文件中定义。以下是几个关键操作:
/* 设置页表项 */
set_pte(pte_t *ptep, pte_t pte);
/* 清除页表项 */
pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep);
/* 检查页表项是否存在 */
int pte_present(pte_t pte);
/* 设置页表项为可写 */
pte_t pte_mkwrite(pte_t pte);
/* 设置页表项为用户态可访问 */
pte_t pte_mkuser(pte_t pte);
/* 设置页表项为不可执行(NX) */
pte_t pte_mknexec(pte_t pte);
/* 获取页表项中的物理页框号 */
unsigned long pte_pfn(pte_t pte);
内核编程提示:直接操作页表是内核中最底层的操作之一,通常只在内存管理子系统(如缺页异常处理、页面回收、内存热插拔)中使用。驱动开发者应使用更高级的API(如
vmalloc、kmalloc),避免直接修改页表。
3.6 本章小结
分页机制是Linux内存管理的基石,理解页表的结构和标志位是深入内核内存管理的前提。本章要点:
- 页表实现虚拟地址到物理地址的转换、内存保护和虚拟内存支持。
- 4级页表(PGD/PUD/PMD/PTE)通过分层索引节省内存,支持按需分配。
- PTE标志位(P、R/W、U/S、A、D、NX、G、PAT)控制页面的状态和权限。
- TLB作为页表的高速缓存,是提升地址转换性能的关键硬件组件。