从虚拟地址到物理页框:Linux 页表与内存管理全解析

📅 发布时间:2026/7/5 17:28:18 👁️ 浏览次数:
从虚拟地址到物理页框:Linux 页表与内存管理全解析
从虚拟地址到物理页框Linux 页表与内存管理全解析以 x86_64 架构为主2025–2026 年主流 Linux 内核视角Linux 内存管理的核心目标隔离进程 高效利用物理内存 按需分配 保护。实现这一切的关键机制就是分页Paging多级页表MMUMemory Management Unit硬件。1. 为什么需要虚拟地址物理内存地址PA是全局的所有进程共享。虚拟地址VA让每个进程认为自己独占整个地址空间通常 0 ~ 2⁶⁴-1。好处进程隔离一个进程无法直接访问另一个进程的内存地址空间布局随机化ASLR写时拷贝COW、按需分页、内存映射文件、huge page 等高级特性x86_64 常用虚拟地址位宽48 位有效范围 0x0000_0000_0000_0000 ~ 0x0000_ffff_ffff_ffff 和 0xffff_0000_0000_0000 ~ 0xffff_ffff_ffff_ffff2. 经典 4KB 页的多级页表结构x86_64 四级页表Linux 在 x86_64 上默认使用4 级页表CONFIG_PGTABLE_LEVELS4 或 5级别英文全称中文常用称呼每项大小每表条目数覆盖虚拟地址范围位段48位 VA第1级Page Global Directory全局页目录 PGD4KB512256TB47–39 位9 bit第2级Page Upper Directory上层目录 PUD4KB512512GB38–30 位9 bit第3级Page Middle Directory中间目录 PMD4KB5121GB29–21 位9 bit第4级Page Table页表 PT / PTE4KB5122MB → 4KB20–12 位9 bit页内偏移————4KB11–0 位12 bit每级页表都是4KB大小一页正好能被上一级指向。每个页表项Entry占8 字节64 位所以 4KB / 8 512 项。总虚拟地址拆分999912 48 位高 16 位用于 canonical 形式。3. 虚拟地址 → 物理地址 的完整转换过程Page Table Walk以虚拟地址0x00007f1234567890为例简化后CPU拿到虚拟地址 VA。从CR3 寄存器读取当前进程的PGD 基地址物理地址。用 VA 的47–39 位作为索引在 PGD 中找到对应条目 → 得到PUD 的物理基地址。用 VA 的38–30 位在 PUD 中索引 → 得到PMD 基地址。用 VA 的29–21 位在 PMD 中索引 → 得到PTE 基地址或直接是大页。用 VA 的20–12 位在 PTE 中索引 → 得到物理页框号PFN 页属性。物理地址 PA PFN 12 | (VA 的 11–0 位偏移)。整个过程由MMU 硬件自动完成软件内核只负责填充页表。4. 页表项PTE / PMD / PUD / PGD常见位含义64 位位含义说明0Present §1 页存在0 缺页触发 page fault1Read/Write (R/W)1 可写0 只读写时拷贝、写保护关键位2User/Supervisor (U/S)1 用户态可访问0 仅内核态7Page Size (PS)在 PMD 级为 1 表示 2MB 大页在 PUD 级为 1 表示 1GB 大页63NX / XDNo-eXecute防止代码执行安全防护51–12Page Frame Number (PFN)物理页框号高位通常为 0 或用于扩展其他Dirty、Accessed、PAT 等脏页标记、访问位、页面属性表等5. 大页Huge Page / THP如何改变页表结构Linux 支持两种大页传统 Huge Pageshugetlbfs2MB / 1GB手动预分配。透明大页THP内核自动尝试合并 4KB 页为 2MB甚至 1GB对应用透明。大页大小生效级别页表减少到几级优势缺点2MBPMD3 级PGD → PUD → PMDTLB 命中率大幅提升页表内存少内存碎片化更严重1GBPUD2 级PGD → PUD极致性能数据库、大数据、虚拟化分配极难碎片化严重THP 默认开启/sys/kernel/mm/transparent_hugepage/enabled在匿名内存、page cache 中自动尝试。6. 关键加速机制TLBTranslation Lookaside Buffer纯页表遍历需要4 次内存访问PGD → PUD → PMD → PTE太慢TLBCPU 内部高速缓存保存最近的 VA → PA 映射。命中1 个周期。未命中走完整 page walk几十上百周期。TLB 大小几十到几千条L1/L2 TLB。大页2MB/1GB大幅减少 TLB miss因为一条 TLB 条目覆盖更大范围。7. 进程切换时页表怎么变每个进程有独立的mm_struct → pgdCR3 指向的基地址。切换进程时内核修改CR3寄存器 → 加载新进程的 PGD 物理地址。现代 CPU 支持PCIDProcess Context IdentifiersASID可以避免每次切换都 flush TLB。8. 常见内核函数与工具调试 / 理解用功能内核函数 / 宏 / 命令说明虚拟地址 → 物理地址__virt_to_phys() / virt_to_phys()内核地址转换物理地址 → 页结构pfn_to_page()PFN → struct page *打印进程页表/proc//pagemap / smaps用户态查看内核调试crash / drgn / gdb vmlinux查看 CR3、页表项强制大页echo always /sys/kernel/mm/transparent_hugepage/enabled开启 THP 激进模式总结一句话记忆流程虚拟地址48位 → 分段索引9999 → 4 次查表PGD → PUD → PMD → PTE → 取出 PFN 偏移 → 物理地址硬件MMU TLB 内核页表填充 缺页中断处理共同完成。想看哪一部分更详细5 级页表5-level paging区别内核地址空间布局直接映射区、高端内存、vmalloc 等缺页异常page fault处理流程ARM64 页表与 x86_64 的主要不同实际用 gdb / crash 看页表的例子随时告诉我继续深入