2. Linux 内存管理基础:虚拟地址与物理地址、页表与 MMU、用户空间与内核空间划分
在深入 mmap 与 DMA 缓冲区共享之前,必须透彻理解 Linux 内存管理的三大基石:虚拟地址与物理地址的映射机制、页表与 MMU 的协同工作、以及用户空间与内核空间的严格划分。这些概念是驱动开发者实现零拷贝、高效数据共享的理论基础。
2.1 虚拟地址与物理地址:为什么需要两层抽象?
物理地址是硬件总线上的真实地址,而虚拟地址是每个进程看到的独立地址空间。引入虚拟地址的核心目的有三:
- 进程隔离:每个进程拥有独立的 32/64 位虚拟地址空间,防止恶意或错误的进程访问其他进程的内存。
- 简化内存管理:物理内存可以是非连续的,但虚拟地址空间对进程表现为连续的线性区域。
- 按需分配:只有真正访问的虚拟页才分配物理页框,提高内存利用率。
mmap 的本质就是在虚拟地址与物理地址之间建立映射,使得用户态可以直接访问 DMA 缓冲区。
下表总结了两种地址的核心差异:
| 特性 | 虚拟地址 (VA) | 物理地址 (PA) |
|---|---|---|
| 可见性 | 每个进程独立 | 全局唯一 |
| 连续性 | 对进程连续 | 可能不连续 |
| 大小 | 受 CPU 位数限制 (如 48 位) | 受硬件总线宽度限制 |
| DMA 使用 | 不可直接使用 | 必须使用 |
| 权限控制 | 通过页表项控制 | 无直接权限控制 |
2.2 页表与 MMU:地址转换的核心引擎
MMU (Memory Management Unit) 是 CPU 内部的硬件单元,负责将虚拟地址实时转换为物理地址。页表 (Page Table) 是 MMU 查询的转换规则表,存储在内存中。每个进程拥有独立的页表。
现代 Linux 使用多级页表(如 x86_64 的 4 级页表)来节省内存。转换过程如下:
- CPU 发出虚拟地址。
- MMU 将虚拟地址拆分为多级索引和偏移量。
- MMU 逐级查询页表,最终找到物理页框号 (PFN)。
- 将 PFN 与页内偏移组合,得到物理地址。
- 如果页表项缺失(缺页异常),触发内核的缺页中断处理程序。
mmap 和 munmap 操作可能导致 TLB 刷新,影响性能。
下图展示了虚拟地址到物理地址的转换流程:
2.3 用户空间与内核空间划分
Linux 将虚拟地址空间划分为用户空间和内核空间两部分。在 32 位系统中,典型的划分是 3:1(3GB 用户空间 + 1GB 内核空间);在 64 位系统中,划分比例不同,但原理一致。
mmap 建立映射,或者使用 copy_to_user/copy_from_user 进行数据拷贝。直接传递内核指针给用户态会导致 oops 或安全漏洞。
下表对比了用户空间与内核空间的关键差异:
| 特性 | 用户空间 | 内核空间 |
|---|---|---|
| 访问权限 | 受限 (Ring 3) | 完全 (Ring 0) |
| 地址范围 (32位) | 0x00000000 - 0xBFFFFFFF | 0xC0000000 - 0xFFFFFFFF |
| 进程隔离 | 每个进程独立 | 所有进程共享 |
| 直接访问硬件 | 不允许 | 允许 |
| 缺页处理 | 可缺页,按需分配 | 通常不可缺页 (原子上下文) |
| mmap 映射 | 可映射文件或设备 | 提供物理内存页框 |
下图展示了典型的 Linux 虚拟地址空间布局:
2.4 页表项 (PTE) 与权限控制
每个页表项 (PTE) 不仅包含物理页框号,还包含关键的权限位和状态位,这些位直接决定了 mmap 的行为:
- Present (P) 位:页面是否在物理内存中。如果为 0,访问将触发缺页异常。
- Read/Write (R/W) 位:页面是否可写。驱动可以通过
mmap的prot参数控制。 - User/Supervisor (U/S) 位:用户态是否可以访问。内核空间页面的 U/S 位为 0。
- Dirty (D) 位:页面是否被写入。DMA 缓冲区通常需要关注此位。
- Access (A) 位:页面是否被访问过,用于页面回收算法。
mmap 操作时,内核会调用 remap_pfn_range() 或 io_remap_pfn_range() 来建立页表项。这些函数会设置 PTE 的物理页框号、缓存属性(如 pgprot_noncached)以及用户态访问权限。
2.5 内存区域 (VMA) 与 mmap 的关系
在 Linux 内核中,每个进程的用户空间由多个 VMA (Virtual Memory Area) 描述。VMA 是内核管理虚拟地址空间的基本单位,记录了地址范围、权限、以及对应的文件或设备操作函数。
当用户态调用 mmap() 时,内核会:
- 在进程的地址空间中查找一个空闲的虚拟地址区间。
- 创建一个新的 VMA 结构,填充起始地址、长度、权限等信息。
- 调用文件或设备的
mmap操作函数(驱动开发者实现)。 - 驱动通过
remap_pfn_range()将物理页框映射到 VMA 的虚拟地址范围。
VMA 与页表的关系如下图所示:
2.6 本章小结
理解虚拟地址与物理地址的映射、页表与 MMU 的转换机制、以及用户空间与内核空间的划分,是掌握 mmap 接口和 DMA 缓冲区共享的前提。驱动开发者必须牢记:
- 用户态只能操作虚拟地址,DMA 只能操作物理地址。
mmap的本质是在用户空间 VMA 与内核物理页框之间建立页表映射。- 页表项的权限位(R/W、U/S)决定了共享缓冲区的访问控制。
- 内核空间与用户空间的严格隔离是系统安全的基础,驱动不能绕过这一层。