x86-64 架构下的四级分页模型

一、分页机制的起源与目标

现代操作系统(如 Linux)采用虚拟内存技术,将程序使用的 “虚拟地址”(Virtual Address, VA)与物理内存的 “物理地址”(Physical Address, PA)分离。这种分离带来两大好处:

  1. 内存隔离:不同程序的虚拟地址空间互不干扰,避免越界访问。
  2. 内存扩展:程序可用的虚拟地址空间可远大于物理内存(通过磁盘交换空间补充)。

分页(Paging)是实现虚拟内存的核心机制之一。它将虚拟地址和物理地址划分为固定大小的 “页”(Page,通常为 4KB),通过 “页表” 记录虚拟页到物理页的映射关系。对于 64 位架构(如 x86-64),由于虚拟地址空间极大(理论上 2⁶⁴字节,约 16EB),单级页表会导致内存浪费(即使只使用少量内存,也需创建完整页表),因此引入多级分页模型,通过分层逐级查找,实现按需加载页表,减少内存占用。

二、x86-64 架构的四级分页结构

x86-64 架构采用四级分页模型,虚拟地址被划分为 5 个部分(包括页内偏移),各级页表逐级索引,最终找到物理页帧(Page Frame)。以下是详细结构:

1. 地址空间基础参数
  • 虚拟地址长度:x86-64 当前常用48 位(Linux 默认,通过CONFIG_PAGE_OFFSET配置,最高可扩展至 57 位),剩余高位用于符号扩展(全 0 或全 1,避免地址空间浪费)。
  • 页大小:默认4KB(2¹² 字节),因此页内偏移占12 位(VA 的最低 12 位)。
  • 各级页表索引位:剩余 48-12=36 位,分为 4 级,每级9 位(早期 x86-64 曾用 10 位,随架构演进调整,当前 Linux 内核源码定义为 9 位)。
2. 四级页表的层级与作用

虚拟地址从高位到低位依次拆分为 4 个索引字段,对应四级页表:

层级名称缩写索引位范围(48 位 VA)作用数据结构(Linux 内核)
第 1 级全局页目录(Page Global Directory)PGD47-39 位指向 “上层页目录表” 的基地址。每个进程对应一个 PGD,存储全局页目录项。pgd_t(本质是指针类型)
第 2 级上层页目录(Page Upper Directory)PUD38-30 位指向 “中间页目录表” 的基地址。在大地址空间中进一步细分。pud_t
第 3 级中间页目录(Page Middle Directory)PMD29-21 位指向 “页表” 的基地址。pmd_t
第 4 级页表(Page Table)PTE20-12 位指向物理页帧的基地址,包含访问权限、缓存策略等标志位。pte_t
页内偏移页内偏移(Page Offset)-11-0 位物理页内的具体偏移地址。-
3. 虚拟地址到物理地址的转换过程

假设虚拟地址为VA = [PGD_INDEX][PUD_INDEX][PMD_INDEX][PTE_INDEX][OFFSET],转换步骤如下:

  1. 第 1 级:全局页目录(PGD)

    • 从当前进程的页目录基址寄存器(CR3,存储 PGD 的物理地址)获取 PGD 的基地址。
    • PGD_INDEX(9 位)索引 PGD,找到对应的PGD 项。若 PGD 项有效(存在上层页目录表),获取 PUD 基地址。
    • 若 PGD 项无效(如未分配内存),触发缺页中断(Page Fault),由内核分配物理页并创建页表。
  2. 第 2 级:上层页目录(PUD)

    • PUD_INDEX(9 位)索引 PUD 表,找到对应的PUD 项,获取 PMD 基地址。
  3. 第 3 级:中间页目录(PMD)

    • PMD_INDEX(9 位)索引 PMD 表,找到对应的PMD 项,获取页表(PTE 表)基地址。
  4. 第 4 级:页表(PTE)

    • PTE_INDEX(9 位)索引 PTE 表,找到对应的PTE 项。若 PTE 项有效,获取物理页帧基地址(PA 的高位部分)。
    • 物理地址 = 物理页帧基地址 + 页内偏移(OFFSET)。
4. 页表项(PTE)的关键标志位

每个 PTE 存储物理页的元数据,重要标志位包括:

  • PRESENT(P):1 位,表示该页是否在物理内存中(0 表示在磁盘交换空间)。
  • RW(读写权限):1 位,0 表示只读,1 表示可写。
  • USER(用户态访问权限):1 位,0 表示仅内核态可访问,1 表示用户态可访问。
  • ACCESSED(A):1 位,页被访问时由硬件自动置 1,用于页面置换算法(如 LRU)。
  • DIRTY(D):1 位,页被写入时置 1,用于脏页回写磁盘。
  • GLOBAL(G):1 位,标记全局页(所有进程可见,如内核代码段),TLB 会缓存此类页。
三、四级分页的内存布局与数据结构

在 Linux 内核中,四级分页通过嵌套的指针结构实现,各级页表本质是数组,每个元素对应一个索引项。

1. 页目录基址寄存器(CR3)
  • 每个进程有独立的 CR3 寄存器,指向其 ** 页目录根(PGD)** 的物理地址。
  • 进程切换时,内核需更新 CR3,切换到新进程的 PGD,实现地址空间隔离。
2. 各级页表的存储
  • PGD:每个进程一个 PGD,通常存储在进程的mm_struct结构体中(mm->pgd)。
  • PUD/PMD/PTE 表:按需动态分配。例如,当访问某个虚拟地址时,若对应页表未创建,内核会分配新的页表(通常为一个物理页,4KB,可存储4KB/8B=512个项,对应 9 位索引,2⁹=512 项)。
3. 内核中的数据结构定义

以下是 Linux 内核源码(arch/x86/include/asm/pgtable_types.h)中的关键定义:

// 页目录项(PGD)、上层页目录项(PUD)、中间页目录项(PMD)本质是指针类型
typedef u64 pgd_t;
typedef u64 pud_t;
typedef u64 pmd_t;
typedef u64 pte_t;

// 页表层级数,x86-64为4级
#define PAGETABLE_LEVELS 4

各级页表的查询函数(arch/x86/include/asm/pgtable.h):

pgd_t pgd_index(struct mm_struct *mm, unsigned long addr);        // 获取PGD索引
pud_t pud_index(pgd_t pgd, unsigned long addr);                  // 获取PUD索引
pmd_t pmd_index(pud_t pud, unsigned long addr);                  // 获取PMD索引
pte_t pte_index(pmd_t pmd, unsigned long addr);                  // 获取PTE索引
四、四级分页的性能优化:TLB 与大页

虽然四级分页需要四次内存访问(查四级页表),但硬件通过 **TLB(Translation Lookaside Buffer,转换旁路缓冲)** 大幅提升效率,软件也可通过 “大页” 减少页表层级。

1. TLB:页表缓存
  • 作用:缓存最近使用的虚拟地址到物理地址的映射,避免每次访问都查页表。
  • 结构:TLB 是硬件缓存,分为指令 TLB数据 TLB,按页大小分为 “小页(4KB)” 和 “大页(2MB/1GB)” 缓存项。
  • 命中流程:访问虚拟地址时,先查 TLB,若命中,直接获取物理地址;若未命中(TLB 缺失),再查四级页表,并将结果写入 TLB。
2. 大页(Huge Page)
  • 原理:使用更大的页(如 2MB 或 1GB),减少页表层级。例如,2MB 大页的页内偏移为 21 位(2²¹=2MB),虚拟地址可跳过 PMD 和 PTE 层级,直接通过 PGD 和 PUD 找到大页。
  • 优势
    • 减少页表数量:大页的页表项直接存储在 PMD 中,无需创建 PTE 表。
    • 提升 TLB 命中率:大页占用 TLB 的一个条目,可映射更大的内存区域,减少 TLB 缺失。
  • Linux 支持:通过hugetlbfs文件系统分配大页,内核配置CONFIG_HUGETLB_PAGES开启。
五、四级分页在 Linux 中的实现细节
1. 页表的创建与销毁
  • 进程创建fork()时复制父进程的 PGD,PUD/PMD/PTE 表按需共享(写时复制)。
  • 内存分配:调用mmap()时,内核通过pgd_alloc()分配 PGD,逐层检查并创建 PUD/PMD/PTE 表(函数链:handle_pte_fault() → pmd_alloc() → pud_alloc() → pgd_alloc())。
  • 内存释放munmap()时反向销毁页表,释放空闲页表的物理内存。
2. 内核地址空间与用户地址空间
  • 用户地址空间(0-3GB,32 位)/(0-128TB,48 位 x86-64):由进程独立的 PGD 管理,不同进程地址空间隔离。
  • 内核地址空间(3-4GB,32 位)/(128TB-16EB,48 位 x86-64):所有进程共享同一套内核页表,PGD 中内核部分固定映射,通过kmap()等函数访问。
3. 页表遍历的内核函数

Linux 提供一组函数用于遍历页表,例如:

// 从虚拟地址获取各级页表项
pgd_t *pgd_offset(struct mm_struct *mm, unsigned long addr);       // 获取PGD项指针
pud_t *pud_offset(pgd_t *pgd, unsigned long addr);                 // 获取PUD项指针
pmd_t *pmd_offset(pud_t *pud, unsigned long addr);                 // 获取PMD项指针
pte_t *pte_offset_map(pmd_t *pmd, unsigned long addr);            // 获取PTE项指针(并映射到内核空间)
六、四级分页的挑战与优化
1. 页表内存占用
  • 若每个进程使用 4KB 小页,四级页表的理论内存占用为:
    • 每级页表项:8B(64 位指针)
    • 总项数:512(PGD) + 512×512(PUD) + 512×512×512(PMD) + 512⁴(PTE) → 显然不可能全部分配!
  • 优化:按需分配(惰性创建),未使用的页表不分配物理内存;大页减少页表层级。
2. TLB 缺失开销
  • 每次 TLB 缺失需访问 4 次内存(查四级页表),耗时约数十纳秒,远高于 TLB 命中(约 1 纳秒)。
  • 优化:利用 TLB 的全局页(G 标志位),减少进程切换时的 TLB 刷新;使用大页提升 TLB 命中率。
3. 地址空间布局随机化(ASLR)
  • 通过随机化 PGD 的基址,增加攻击者预测虚拟地址的难度,提升安全性。Linux 通过/proc/sys/kernel/randomize_va_space控制。
七、对比其他架构:分页级数的差异

x86-64 的四级分页并非唯一方案,不同架构根据地址空间需求设计分页级数:

  • ARMv8(64 位):默认三级分页(虚拟地址 48 位,页大小 4KB,三级索引各 12 位),可扩展至四级(针对更大地址空间)。
  • PowerPC(64 位):支持三级或四级分页,取决于页大小和地址空间配置。
  • x86-32(32 位):早期使用两级分页(PGD 和 PTE),页大小 4KB,虚拟地址空间 4GB。
八、总结:四级分页的核心价值
  1. 分层管理:将超大虚拟地址空间拆解为四级索引,避免单级页表的内存爆炸问题。
  2. 按需分配:仅为已使用的虚拟地址创建页表,节省内存。
  3. 兼容性与扩展性:支持 48 位 / 57 位虚拟地址,适应 64 位架构的长期演进。
  4. 性能平衡:通过 TLB 和大页技术,在地址转换效率与内存占用间找到平衡。

形象比喻:把四级分页想象成 “快递分拣四件套”

你可以把计算机的 “虚拟地址到物理地址转换” 想象成一次快递配送过程,而四级分页模型就是快递从 “虚拟收件地址” 到 “真实房间地址” 的四级分拣系统。

1. 场景设定
  • 你的快递地址(虚拟地址):广东省广州市天河区珠江新城 1 栋 1001 室
  • 真实仓库地址(物理地址):仓库货架第 8 区第 3 排第 5 层第 2 号格子

快递员看不懂 “虚拟地址”,需要通过四级分拣中心,层层拆解地址,最终找到真实位置。

2. 四级分拣中心(对应四级分页)
分拣层级对应分页术语作用类比地址拆解
第 1 级:市级分拣站全局页目录(PGD, Page Global Directory)确定 “广东省”,缩小到某个大区域。每个省对应一个 “分拣账本”(页目录表)。从 “广东省” 找到对应的第一级账本索引
第 2 级:区级中转站上层页目录(PUD, Page Upper Directory)确定 “广州市天河区”,在大区域内进一步细分。每个区对应账本中的一条记录。从 “广州市天河区” 找到第二级索引
第 3 级:街道分拨点中间页目录(PMD, Page Middle Directory)确定 “珠江新城”,在区内缩小到具体街道。每个街道对应一条记录。从 “珠江新城” 找到第三级索引
第 4 级:小区快递柜页表项(PTE, Page Table Entry)确定 “1 栋 1001 室”,最终定位到具体房间。房间号对应物理地址的 “偏移量”。从 “1 栋 1001 室” 找到物理地址的具体位置
3. 核心原理
  • 虚拟地址是 “快递面单上的假地址”:程序看到的地址是虚拟的,方便管理(比如多个程序可以共用 “1001 室”,互不干扰)。
  • 四级分页是 “地址翻译器”:每一级都像一本字典,通过 “索引”(类似地址中的区、街道、门牌号)层层查找,最终找到物理内存的真实位置。
  • 为什么需要四级?:如果地址空间太大(比如 64 位系统的内存地址长达 16EB),一级分拣根本管不过来,必须分层管理,就像大城市需要区→街道→小区→房间四级地址,否则快递员会疯掉!
4. 一句话总结

四级分页就是把一个超长的虚拟地址拆成四段,每一段对应一个 “地址字典”,通过四次翻字典,把 “假地址” 翻译成 “真实内存地址”,就像快递员按地址四级分拣找到你家一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值