news 2026/5/1 10:35:37

Linux 页表机制详解(x86_64 架构)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux 页表机制详解(x86_64 架构)

前言

在 AI 计算和推理加速的内核驱动开发中,高效的内存管理是构建高性能计算系统的核心基础。无论是 GPU 显存映射、DMA 缓冲区管理,还是大规模张量数据的虚拟地址分配,都离不开对页表机制的深入理解。本文系统梳理 x86_64 架构下 Linux 的四级页表机制,为 AI 计算相关的内核驱动开发提供理论基础和实践参考。

1. 概述

在 x86_64 架构的 Linux 系统中,页表采用四级分页模型来实现虚拟地址到物理地址的转换。虽然现代硬件支持五级页表,但 Linux 默认启用四级分页,以多级页表项的形式分层存储在物理内存中。

2. 四级页表结构

Linux 对 x86_64 的虚拟地址(默认 48 位有效地址)进行分段,依次索引四级页表, 页表层级说明如下:

页表层级全称索引位宽虚拟地址位段作用
PGD页全局目录 (Page Global Directory)9 位47~39索引 PGD 项,指向 PUD 页的物理地址
PUD页上级目录 (Page Upper Directory)9 位38~30索引 PUD 项,指向 PMD 页的物理地址
PMD页中间目录 (Page Middle Directory)9 位29~21索引 PMD 项,指向 PT 页或 2MB 大页面
PT页表 (Page Table)9 位20~12索引 PT 项,指向 4KB 物理页面
页内偏移-12 位11~0物理页面内的字节偏移

2.1. 各级页表详解

  1. PGD(页全局目录)

    • 进程的mm_struct中保存 PGD 物理地址
    • 虚拟地址最高 9 位索引 PGD 项
    • 指向 PUD 页的物理地址
  2. PUD(页上级目录)

    • 中间层级,9 位索引 PUD 项
    • 指向 PMD 页物理地址
    • 在 4KB 页面且地址范围较小时常与 PGD 合并
  3. PMD(页中间目录)

    • 9 位索引 PMD 项
    • 可指向 PT 页物理地址
    • 或直接映射 2MB 大页面(此时跳过 PT 层级)
  4. PT(页表)

    • 最低层级,9 位索引 PT 项
    • 直接指向 4KB 物理页面的起始地址
    • 虚拟地址最低 12 位为页内偏移量

3. 页表项(PTE)存储格式

每个页表项占8 字节(64 位),包含两部分核心信息:

页表项(64 位 / 8 字节)结构: ┌────────────────────────────────────────────────────────────────┐ │ 63 0 │ ├──────────────────────────────────────────┬─────────────────────┤ │ 物理页面基地址 (52 位) │ 标志位 (12 位) │ │ [63:12] │ [11:0] │ └──────────────────────────────────────────┴─────────────────────┘ 详细位段分布: ┌──┬──┬──┬──┬──────────────────────────────────┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ │N │PK│PK│PK│ │ │P │ │ │P │P │U │R │ │ │ │P │ │X │3 │2 │1 │ 物理页面基地址 │G │A │D │A │C │W │/ │/ │ │ │ │ │ │ │ │ │ │ (Physical Page Base Address) │ │T │ │ │D │T │S │W │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ├──┼──┼──┼──┼──────────────────────────────────┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┼──┤ │63│62│61│60│59 12│11│10│ 9│ 8│ 7│ 6│ 5│ 4│ 3│ 2│ 1│ 0│ └──┴──┴──┴──┴──────────────────────────────────┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘

3.1. 物理地址部分(位 63-12)

  • 高 52 位:目标物理页的基地址
  • 物理地址仅低 52 位有效,由 CPU 架构决定
  • 由于页面按 4KB 对齐,低 12 位始终为 0,因此不需要存储

3.2. 权限与状态标志位(位 11-0)

标志位名称作用
0P存在位 (Present)标记页是否在物理内存中
1R/W读写权限 (Read/Write)0=只读,1=可读写
2U/S用户/内核空间 (User/Supervisor)0=仅内核态,1=用户态可访问
3PWT写通 (Page Write-Through)控制写缓存策略
4PCD禁用缓存 (Page Cache Disable)1=禁用页面缓存
5A访问位 (Accessed)标记页是否被访问过
6D脏位 (Dirty)标记页是否被修改过
7PAT页面属性 (Page Attribute Table)与 PWT/PCD 配合控制缓存
8G全局位 (Global)1=全局页,TLB 刷新时保留
9-11AVL可用位 (Available)操作系统自定义使用
62RSV保留位保留给未来扩展
63NX禁止执行 (No eXecute)1=禁止从该页执行代码

4. 页表空间占用

在 x86_64 架构的 Linux 四级分页模型中,每级页表默认占用 4KB 物理内存

4.1. 空间计算逻辑

  • 每个页表项(PTE/PMD/PUD/PGD 项)占8 字节
  • 一个 4KB 页面可容纳:4096 ÷ 8 = 512 个页表项
  • 虚拟地址中每级页表的索引位宽为9 位
  • 2^9 = 512,刚好匹配一个 4KB 页面能存储的页表项数量

因此,PGD、PUD、PMD、PT 各级页表只要独立存在,单级占用空间都是4KB

4.2. 特殊情况

  1. 2MB 大页面

    • PMD 项直接映射物理大页
    • 跳过 PT 层级,节省 4KB 的 PT 页空间
  2. 1GB 超大页面

    • PUD 项直接映射物理超大页
    • 跳过 PMD 和 PT 层级,节省两级页表空间
  3. 地址范围较小时

    • PUD 层会与 PGD 层合并
    • PUD 无需单独占用 4KB 空间

5. 内核与用户页表隔离

5.1. 用户进程页表

  • 每个进程有独立的 PGD
  • 用户空间(0x0000000000000000~0x00007FFFFFFFFFFF)的页表项由进程私有
  • 进程切换时只需切换CR3 寄存器指向新进程的 PGD 物理地址

5.2. 内核页表

  • 内核空间(0xFFFF800000000000及以上)的页表项在所有进程的 PGD/PUD/PMD 中共享
  • 实现内核地址空间的全局映射
  • 避免每次进程切换时重新映射内核空间

6. 硬件关联与管理

6.1. 硬件支持

  • CR3 寄存器:存储当前进程 PGD 的物理地址
  • CPU 地址转换:自动遍历四级页表完成虚拟地址到物理地址的转换
  • TLB(Translation Lookaside Buffer):缓存虚拟地址到物理地址的映射,减少页表遍历开销

6.2. 内核管理

6.2.1. 关键数据结构
/* 进程内存描述符 - 定义在 include/linux/mm_types.h */structmm_struct{pgd_t*pgd;/* 指向进程的页全局目录(页表根) *//* ... 更多字段 ... */};/* 页表项类型定义 - 定义在 arch/x86/include/asm/pgtable_types.h *///所有的页表项都是8字节的一个定义typedefstruct{unsignedlongpgd;}pgd_t;/* 页全局目录项 */typedefstruct{unsignedlongpud;}pud_t;/* 页上级目录项 */typedefstruct{unsignedlongpmd;}pmd_t;/* 页中间目录项 */typedefstruct{unsignedlongpte;}pte_t;/* 页表项 */
6.2.2. 关键管理函数

页表分配函数

/* 分配各级页表 */pgd_t*pgd_alloc(structmm_struct*mm);/* 分配页全局目录 */int__pud_alloc(structmm_struct*mm,pgd_t*pgd,unsignedlongaddress);int__pmd_alloc(structmm_struct*mm,pud_t*pud,unsignedlongaddress);int__pte_alloc(structmm_struct*mm,pmd_t*pmd);/* 页面分配 */structpage*alloc_pages(gfp_tgfp_mask,unsignedintorder);void__free_pages(structpage*page,unsignedintorder);

页表遍历与查找函数

/* 根据虚拟地址查找各级页表项 */pgd_t*pgd_offset(structmm_struct*mm,unsignedlongaddress);pud_t*pud_offset(pgd_t*pgd,unsignedlongaddress);pmd_t*pmd_offset(pud_t*pud,unsignedlongaddress);pte_t*pte_offset_map(pmd_t*pmd,unsignedlongaddress);/* 页表项状态检查 */intpte_present(pte_tpte);/* 检查页是否在内存中 */intpte_young(pte_tpte);/* 检查访问位 */intpte_dirty(pte_tpte);/* 检查脏位 */intpte_write(pte_tpte);/* 检查写权限 */

页表项修改函数

/* 设置页表项 */voidset_pte(pte_t*ptep,pte_tpte);voidset_pmd(pmd_t*pmdp,pmd_tpmd);voidset_pud(pud_t*pudp,pud_tpud);voidset_pgd(pgd_t*pgdp,pgd_tpgd);/* 页表项属性修改 */pte_tpte_mkwrite(pte_tpte);/* 设置可写 */pte_tpte_wrprotect(pte_tpte);/* 设置写保护 */pte_tpte_mkdirty(pte_tpte);/* 设置脏位 */pte_tpte_mkyoung(pte_tpte);/* 设置访问位 */

页表释放函数

/* 释放各级页表 */voidpgd_free(structmm_struct*mm,pgd_t*pgd);voidpud_free(structmm_struct*mm,pud_t*pud);voidpmd_free(structmm_struct*mm,pmd_t*pmd);voidpte_free(structmm_struct*mm,pgtable_tpte);

TLB 管理函数

/* TLB 刷新操作 *//* 刷新所有 CPU 的 TLB */voidflush_tlb_all(void);/* 刷新指定进程的 TLB */voidflush_tlb_mm(structmm_struct*mm);voidflush_tlb_page(structvm_area_struct*vma,unsignedlongaddr);voidflush_tlb_range(structvm_area_struct*vma,unsignedlongstart,unsignedlongend);

7. 地址转换实例

7.1. 示例参数设定

假设需要转换虚拟地址:0x0000 1234 5678 ABCD

已知条件:

  • 进程 PGD 物理地址:0x100000
  • 页表项标志位:P=1(存在)、RW=1(可读写)
  • 高 52 位为物理地址基址

7.2. 步骤 1:拆分虚拟地址位段

将 48 位虚拟地址0x000012345678ABCD按位段拆分:

虚拟地址:0x0000 1234 5678 ABCD ↓ ↓ ↓ ↓ 二进制: 0000 0000 0000 0001 0010 0011 0100 0101 0110 0111 1000 1010 1011 1100 1101 位段拆分: ├─ PGD 索引 [47:39]:0x000 (十进制: 0) ├─ PUD 索引 [38:30]:0x024 (十进制: 36) ├─ PMD 索引 [29:21]:0x08D (十进制: 141) ├─ PT 索引 [20:12]:0x171 (十进制: 369) └─ 页内偏移 [11:0] :0xBCD (十进制: 3021)

7.3. 步骤 2:四级页表逐级寻址

第一级:寻址 PGD 项
PGD 物理地址 = 0x100000 PGD 索引 = 0x000 PGD 项物理地址 = 0x100000 + (0x000 × 8) = 0x100000 假设 PGD 项值 = 0x110000_000000067 └─ 高 52 位 0x110000 → PUD 页的物理地址
第二级:寻址 PUD 项
PUD 页物理地址 = 0x110000 PUD 索引 = 0x024 PUD 项物理地址 = 0x110000 + (0x024 × 8) = 0x110120 假设 PUD 项值 = 0x120000_000000067 └─ 高 52 位 0x120000 → PMD 页的物理地址
第三级:寻址 PMD 项
PMD 页物理地址 = 0x120000 PMD 索引 = 0x08D PMD 项物理地址 = 0x120000 + (0x08D × 8) = 0x120438 假设 PMD 项值 = 0x130000_000000067 └─ 高 52 位 0x130000 → PT 页的物理地址
第四级:寻址 PT 项
PT 页物理地址 = 0x130000 PT 索引 = 0x171 PT 项物理地址 = 0x130000 + (0x171 × 8) = 0x130888 假设 PT 项值 = 0x140000_000000067 └─ 高 52 位 0x140000 → 最终物理页面的基地址

7.4. 步骤 3:计算最终物理地址

物理地址 = 物理页面基地址 + 页内偏移 = 0x140000 + 0xBCD = 0x140BCD

7.5. 转换流程图

虚拟地址: 0x000012345678ABCD ↓ ┌─────────────────────────────────────┐ │ CR3 寄存器: 0x100000 (PGD 基址) │ └─────────────────────────────────────┘ ↓ [PGD 索引: 0x000] ┌─────────────────────────────────────┐ │ PGD[0] = 0x110000_000000067 │ │ → PUD 基址: 0x110000 │ └─────────────────────────────────────┘ ↓ [PUD 索引: 0x024] ┌─────────────────────────────────────┐ │ PUD[36] = 0x120000_000000067 │ │ → PMD 基址: 0x120000 │ └─────────────────────────────────────┘ ↓ [PMD 索引: 0x08D] ┌─────────────────────────────────────┐ │ PMD[141] = 0x130000_000000067 │ │ → PT 基址: 0x130000 │ └─────────────────────────────────────┘ ↓ [PT 索引: 0x171] ┌─────────────────────────────────────┐ │ PT[369] = 0x140000_000000067 │ │ → 物理页基址: 0x140000 │ └─────────────────────────────────────┘ ↓ [页内偏移: 0xBCD] ┌─────────────────────────────────────┐ │ 最终物理地址: 0x140BCD │ └─────────────────────────────────────┘

8. 性能优化机制

8.1. TLB 缓存

  • TLB 是 CPU 内部的高速缓存,存储最近使用的虚拟地址到物理地址的映射
  • 命中 TLB 时无需遍历四级页表,大幅提升地址转换速度
  • TLB 失效时才触发页表遍历(Page Table Walk)

8.2. 大页面支持

  • 2MB 大页面:减少页表层级,降低 TLB 压力
  • 1GB 超大页面:适用于大内存应用,进一步减少页表开销
  • 通过hugetlbfs文件系统或mmapMAP_HUGETLB标志使用

8.3. 页表缓存

  • 内核维护页表缓存池,避免频繁分配和释放页表页
  • 使用 slab 分配器管理页表项

9. 总结

Linux 的四级页表机制通过分层索引的方式,实现了灵活、高效的虚拟地址到物理地址的转换。整个过程由 CPU 硬件自动完成,配合 TLB 缓存和大页面支持,在保证内存隔离和保护的同时,提供了出色的性能表现。

关键要点:

  1. 四级页表结构:PGD → PUD → PMD → PT → 物理页
  2. 每级页表占用 4KB 物理内存,每个页表项 8 字节
  3. 48 位虚拟地址分段索引,12 位页内偏移
  4. CR3 寄存器存储 PGD 基址,TLB 缓存加速转换
  5. 支持大页面优化,实现进程隔离和内存保护

这套机制是现代操作系统内存管理的基石,为应用程序提供了统一、安全、高效的内存访问接口。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/30 14:09:44

数据流动的艺术:用可视化技术讲述数据故事

数据流动的艺术:用可视化技术讲述数据故事 【免费下载链接】ggsankey Make sankey, alluvial and sankey bump plots in ggplot 项目地址: https://gitcode.com/gh_mirrors/gg/ggsankey 在当今数据驱动的时代,我们常常面临这样的挑战:…

作者头像 李华
网站建设 2026/4/30 23:11:47

uvloop 终极指南:Python 异步编程性能提升 10 倍的秘诀

uvloop 终极指南:Python 异步编程性能提升 10 倍的秘诀 【免费下载链接】uvloop Ultra fast asyncio event loop. 项目地址: https://gitcode.com/gh_mirrors/uv/uvloop uvloop 是 Python 生态中性能最强的异步事件循环库,作为标准 asyncio 的直接…

作者头像 李华
网站建设 2026/5/1 6:47:29

终极指南:快速将Vite应用接入Garfish微前端框架

终极指南:快速将Vite应用接入Garfish微前端框架 【免费下载链接】garfish A powerful micro front-end framework 🚚 项目地址: https://gitcode.com/gh_mirrors/ga/garfish 在现代前端开发中,微前端架构已经成为大型应用团队协作的首…

作者头像 李华
网站建设 2026/5/1 5:52:52

解析 React 中的“错误边界(Error Boundary)”:为什么它不能捕获异步代码或事件处理函数中的错误?

欢迎来到本次关于React错误边界(Error Boundary)的深度解析讲座。在构建复杂的单页应用时,我们都曾面临用户界面突然崩溃、显示空白页面的窘境。React的错误边界机制正是为了解决这一痛点而生,它旨在提供一种在组件树中捕获错误、…

作者头像 李华
网站建设 2026/5/1 5:52:44

PlayIntegrityFix完整教程:2025年解决Google认证失败的最佳方案

还在为Google Play设备认证失败而苦恼吗?PlayIntegrityFix作为2025年最有效的Play Integrity修复工具,能够彻底解决设备验证问题,让你的Root设备重新获得完整认证。本教程将详细介绍如何快速安装配置这一强大工具,解决各种认证失败…

作者头像 李华
网站建设 2026/5/1 6:36:10

RouterOS 7.19.2 arm64部署与优化全攻略:从安装到高性能调优

RouterOS 7.19.2 arm64版本为企业级网络设备管理带来了革命性的性能提升和硬件兼容性突破。本文将从实战角度出发,提供完整的部署指南、性能优化策略和安全加固方案,帮助网络管理员快速构建稳定高效的网络基础设施。 【免费下载链接】MikroTikPatch 项…

作者头像 李华