2. 虚拟内存与地址空间:虚拟地址、物理地址、线性地址转换过程、x86_64架构下的4级页表结构
2.1 地址空间的基本概念
在Linux内核中,内存管理最核心的抽象就是虚拟内存。每个用户态进程都拥有独立的虚拟地址空间,而内核本身也占据固定的高位地址区域。这种隔离机制使得进程之间互不干扰,同时允许每个进程认为自己拥有连续的、从0开始的巨大内存空间。
- 虚拟地址 (VA):程序指令中使用的地址,由CPU的MMU(内存管理单元)解释。
- 物理地址 (PA):实际硬件总线上的地址,用于访问DRAM或MMIO设备。
- 线性地址 (LA):x86架构中的中间地址。在启用分页后,线性地址经过页表转换得到物理地址;在不启用分页时,线性地址直接等于物理地址。
在x86_64架构下,Linux内核采用平坦内存模型,即代码段、数据段、堆栈段都使用相同的基地址(0),段机制被最小化使用,主要依赖分页机制完成地址转换。
2.2 虚拟地址到物理地址的转换过程
地址转换是一个多级查表过程。以x86_64的4级页表为例,一个48位的虚拟地址被划分为5个部分:4个页表索引(各9位)和1个页内偏移(12位)。
2.3 x86_64 4级页表结构详解
x86_64架构使用4级页表,每级页表包含512个条目(2^9),每个条目占8字节,因此每级页表大小为4KB(恰好一个物理页框)。页表条目(PTE)中包含了下一级页表的物理地址以及权限位、存在位、访问位等标志。
| 页表级别 | 缩写 | 索引位数 | 覆盖范围 | 条目数 |
|---|---|---|---|---|
| 第1级(顶层) | PML4 | 9位 | 512 GB | 512 |
| 第2级 | PDPT | 9位 | 1 GB | 512 |
| 第3级 | PD | 9位 | 2 MB | 512 |
| 第4级(底层) | PT | 9位 | 4 KB | 512 |
每个页表条目(PTE)的格式如下(64位):
位 63:52 — 保留/忽略(某些架构用于NX位或未来扩展)
位 51:12 — 物理页框号(PFN),即物理地址的高40位
位 11:9 — 忽略(AVL位,供操作系统使用)
位 8 — 全局位(G)
位 7 — 页面大小(PS,用于大页)
位 6 — 脏位(D)
位 5 — 访问位(A)
位 4 — 页级缓存禁用(PCD)
位 3 — 页级写透缓存(PWT)
位 2 — 用户/超级用户位(U/S)
位 1 — 读/写位(R/W)
位 0 — 存在位(P)
2.4 线性地址转换过程(带TLB加速)
每次内存访问都遍历4级页表会带来巨大的性能开销。因此,CPU内部集成了TLB(Translation Lookaside Buffer,转译后备缓冲器),用于缓存最近使用的虚拟地址到物理地址的映射。
2.5 Linux内核中的地址空间划分
在x86_64架构下,Linux内核将虚拟地址空间划分为用户空间和内核空间。典型的划分比例为:用户空间占低47位(0x0000_0000_0000_0000 ~ 0x7FFF_FFFF_FFFF_FFFF),内核空间占高47位(0xFFFF_8000_0000_0000 ~ 0xFFFF_FFFF_FFFF_FFFF)。中间区域为non-canonical地址,不可访问。
| 区域 | 地址范围 | 用途 |
|---|---|---|
| 用户空间 | 0x0000_0000_0000_0000 ~ 0x7FFF_FFFF_FFFF_FFFF | 用户态进程代码、数据、堆、栈、mmap区域 |
| 内核空间(直接映射) | 0xFFFF_8000_0000_0000 ~ 0xFFFF_87FF_FFFF_FFFF | 线性映射物理内存(PHYSICAL_START + offset) |
| 内核空间(vmalloc) | 0xFFFF_8800_0000_0000 ~ 0xFFFF_8FFF_FFFF_FFFF | 非连续内存分配区域 |
| 内核空间(固定映射) | 0xFFFF_9000_0000_0000 ~ 0xFFFF_97FF_FFFF_FFFF | 固定映射(如ACPI、IO APIC) |
| 内核空间(模块) | 0xFFFF_9800_0000_0000 ~ 0xFFFF_9FFF_FFFF_FFFF | 内核模块加载区域 |
0x7F00_1234_5678,MMU将其分解为:
- PML4索引:
0x1FC(9位) - PDPT索引:
0x000(9位) - PD索引:
0x123(9位) - PT索引:
0x456(9位) - 页内偏移:
0x678(12位)
0xABCDE),拼接偏移后得到物理地址 0xABCDE_678。
2.6 大页支持(Huge Pages)
为了减少TLB未命中并降低页表遍历开销,x86_64支持大页。在页表条目中设置PS位(Page Size)可以跳过一级或多级页表,直接映射更大的连续物理内存。
| 页大小 | 页表级别 | 覆盖范围 | TLB条目覆盖 |
|---|---|---|---|
| 4 KB | 4级(标准) | 4 KB | 小 |
| 2 MB | 3级(在PD级设置PS位) | 2 MB | 中 |
| 1 GB | 2级(在PDPT级设置PS位) | 1 GB | 大 |
Linux内核通过hugetlbfs文件系统或透明大页(Transparent Huge Pages, THP)机制来利用大页特性,显著提升数据库、HPC等内存密集型应用的性能。