2. Linux 内存管理基础:虚拟地址空间、物理内存、页表与 MMU 工作原理

在深入 Android HAL 层 mmap 实战之前,必须透彻理解 Linux 内存管理的核心基石。HAL 层驱动通过 mmap 实现用户空间与内核空间的数据零拷贝传输,其本质就是操作虚拟地址与物理地址的映射关系。本章将系统梳理虚拟地址空间、物理内存、页表及 MMU 的工作原理,为后续 mmap 机制剖析打下坚实的理论基础。

2.1 虚拟地址空间:进程的“内存幻象”

每个用户态进程都拥有独立的 32 位或 64 位虚拟地址空间。在 32 位 ARM Linux 中,虚拟地址空间通常按 3:1 比例划分:用户空间 0x00000000 – 0xBFFFFFFF(3GB),内核空间 0xC0000000 – 0xFFFFFFFF(1GB)。64 位架构下空间更大,但划分逻辑类似。

核心概念:虚拟地址空间是操作系统为每个进程提供的抽象内存视图。进程“以为”自己独占整个内存地址范围,实际上物理内存由内核统一管理,通过页表实现虚实转换。
32位 Linux 虚拟地址空间布局 用户空间 (0x00000000 – 0xBFFFFFFF) 代码段 | 数据段 | 堆 | 栈 | 共享库 | mmap 映射区 大小: 3GB (0xC0000000 字节) 内核空间 (0xC0000000 – 0xFFFFFFFF) 内核代码 | 驱动模块 | 页表 | 内核栈 | 物理内存直接映射区 大小: 1GB (0x40000000 字节) ▲ 分界线 0xC0000000 进程只能访问用户空间,内核空间通过系统调用或异常进入

2.2 物理内存:真实的硬件资源

物理内存是 DRAM 芯片提供的真实存储介质,以字节为单位编址。在嵌入式 Android 设备中,物理内存通常从 0x80000000 或 0x40000000 开始映射。物理内存被划分为固定大小的页框(Page Frame),Linux 内核以页为单位管理物理内存,标准页大小为 4KB。

内存区域 物理地址范围(典型) 用途
DMA 区域 (ZONE_DMA) 0x00000000 – 0x00FFFFFF DMA 设备直接访问
普通区域 (ZONE_NORMAL) 0x01000000 – 0x3FFFFFFF 内核直接映射、用户进程
高端内存 (ZONE_HIGHMEM) 0x40000000 以上 32 位系统访问 >1GB 物理内存
注意:在 64 位 ARM 系统中,物理地址空间远大于虚拟地址空间,ZONE_HIGHMEM 不再需要。但页表层级增加为 4 级(PGD → PUD → PMD → PTE),转换开销更大。

2.3 页表:虚实转换的“翻译官”

页表(Page Table)是操作系统维护的多级数据结构,用于将虚拟地址映射到物理地址。每个进程拥有独立的页表,存储在物理内存中,由 MMU 硬件解析。页表项(PTE)包含物理页框号、权限位、存在位、脏位等关键信息。

多级页表虚实地址转换流程 虚拟地址 (32位) PGD 索引 (10位) PTE 索引 (10位) 页内偏移 (12位) PGD 页表 PTE 页表 物理页框 4KB PGD 表项指向 PTE 页表基址 PTE 表项指向 物理页框号

2.4 MMU:硬件加速的地址转换引擎

内存管理单元(Memory Management Unit)是 CPU 内部的硬件模块,负责自动完成虚拟地址到物理地址的转换。MMU 的工作流程如下:

  1. TLB 查找:CPU 发出虚拟地址,MMU 首先在 TLB(Translation Lookaside Buffer,快表)中查找缓存项。TLB 命中则直接获得物理地址,延迟仅 1-2 个时钟周期。
  2. 页表遍历:TLB 未命中时,MMU 硬件自动遍历多级页表,逐级读取 PGD、PUD、PMD、PTE 表项,最终找到物理页框号。
  3. 权限检查:MMU 同时检查 PTE 中的权限位(读/写/执行)和特权级,非法访问触发缺页异常或段错误。
  4. 物理地址生成:将物理页框号与虚拟地址中的页内偏移拼接,形成完整的物理地址,发送给内存控制器。
性能关键:TLB 命中率直接影响内存访问性能。HAL 层 mmap 映射大块内存时,会使用大页(如 2MB 或 1GB 页)减少页表项数量,提高 TLB 覆盖范围,这是驱动优化的核心手段之一。

2.5 缺页异常与物理内存分配

当进程访问的虚拟地址尚未建立有效页表映射时,MMU 无法完成转换,触发缺页异常(Page Fault)。Linux 内核缺页异常处理程序执行以下步骤:

异常类型 触发条件 内核处理动作
有效缺页 VMA 存在但 PTE 无效 分配物理页框,建立映射,返回用户空间
写时复制缺页 共享页被写入 复制物理页,更新 PTE 为可写
无效缺页 VMA 不存在或权限不符 发送 SIGSEGV 信号,进程终止
缺页异常处理流程 CPU 访问虚拟地址 MMU 缺页异常 内核缺页异常处理程序 分配物理页框 建立页表映射 发送 SIGSEGV 进程终止

2.6 内核空间与用户空间的数据交互

在 Android HAL 层驱动中,mmap 的核心价值在于绕过内核空间的数据拷贝,让用户空间直接访问物理内存或 DMA 缓冲区。理解以下关键点:

  • 内核直接映射区:在 32 位系统中,物理内存前 896MB 被线性映射到内核空间 0xC0000000 起始的虚拟地址,phys_to_virt() 和 virt_to_phys() 实现简单转换。
  • ioremap:外设寄存器物理地址通过 ioremap 映射到内核虚拟地址空间,HAL 层驱动通过 mmap 将同一段物理地址映射到用户空间。
  • DMA 缓冲区:使用 dma_alloc_coherent() 分配物理连续且 cache 一致的内存,通过 mmap 导出给用户空间,实现零拷贝数据传输。
实战要点:在编写 HAL 层 mmap 实现时,驱动中需要实现 mmap 文件操作,调用 remap_pfn_range() 或 dma_mmap_coherent() 将物理页映射到用户虚拟地址空间。理解页表结构和 MMU 行为是正确实现这些接口的前提。

2.7 本章小结