2. Linux 内存管理基础:虚拟地址、物理地址、MMU、页表、页框、页大小
在编写 mmap 驱动之前,必须透彻理解 Linux 内存管理的核心概念。mmap 的本质是在用户空间虚拟地址与物理内存(或设备 I/O 内存)之间建立映射,而这一过程完全依赖于硬件 MMU 和内核页表机制。本节将逐一拆解这些基础构件。
2.1 虚拟地址与物理地址
现代操作系统普遍采用虚拟内存管理。每个用户态进程拥有独立的 32 位或 64 位虚拟地址空间,而物理内存则由所有进程共享。
| 概念 | 说明 | 典型范围(32位) |
|---|---|---|
| 虚拟地址 (VA) | 进程看到的线性地址,由 CPU 生成 | 0x00000000 – 0xFFFFFFFF |
| 物理地址 (PA) | 实际硬件总线上的地址,对应 RAM 或 I/O 寄存器 | 0x00000000 – 0xFFFFFFFF |
| 用户空间 | 进程可访问的虚拟地址范围 | 0x00000000 – 0xBFFFFFFF (3G) |
| 内核空间 | 内核代码和数据驻留的虚拟地址范围 | 0xC0000000 – 0xFFFFFFFF (1G) |
关键认知: 用户程序永远不能直接操作物理地址。所有内存访问都经过虚拟地址 → MMU → 物理地址的转换。mmap 驱动的作用就是让内核帮助用户程序建立这种转换关系,从而安全地访问物理内存或设备寄存器。
2.2 MMU:内存管理单元
MMU(Memory Management Unit)是 CPU 内部负责地址转换的硬件模块。它接收 CPU 发出的虚拟地址,通过查询页表(Page Table)输出对应的物理地址。
MMU 核心职责:
- 地址转换: 将虚拟地址映射到物理地址。
- 权限检查: 检查当前访问是否违反页表权限(读/写/执行)。
- 缓存控制: 控制页面的 Cache 策略(Write-Back / Write-Through / Uncached)。
- 异常触发: 缺页(Page Fault)时通知内核加载页面。
2.3 页表与页框
页表是 MMU 进行地址转换的依据,存储在物理内存中。每个进程拥有独立的页表,由内核维护。页表项(PTE)记录了虚拟页号到物理页框号的映射关系。
2.3.1 页框(Page Frame)
物理内存被划分为固定大小的块,称为页框(或物理页)。Linux 内核使用 struct page 结构体管理每一个物理页框。
2.3.2 页大小(Page Size)
Linux 默认页大小为 4KB(ARM64 和 x86_64 均支持)。部分架构支持大页(Huge Pages):2MB 或 1GB,用于减少 TLB 缺失。
| 页大小 | 典型用途 | TLB 覆盖范围 |
|---|---|---|
| 4 KB | 通用进程内存、堆、栈 | 小 |
| 2 MB | 大页映射、数据库、虚拟化 | 中 |
| 1 GB | 超大内存映射、GPU 显存 | 大 |
2.4 多级页表结构
为了节省页表占用的物理内存,现代 CPU 采用多级页表。以 x86_64 为例,使用 4 级页表:PML4 → PDPT → PD → PT。
2.5 内核中的页表操作接口
驱动开发中,虽然不直接操作页表硬件,但需要理解内核提供的页表查询和映射函数:
// 内核虚拟地址 → 物理页框号
unsigned long virt_to_phys(volatile void *address);
// 物理地址 → 内核虚拟地址(直接映射区)
void *phys_to_virt(unsigned long address);
// 建立页表映射(内核内部使用)
int remap_pfn_range(struct vm_area_struct *vma,
unsigned long addr,
unsigned long pfn,
unsigned long size,
pgprot_t prot);
// 获取页表项内容
pte_t *pte_offset_map(pmd_t *pmd, unsigned long address);
驱动开发注意: 用户空间无法直接调用上述函数。mmap 驱动通过实现 file_operations 中的
.mmap 回调,在回调内调用 remap_pfn_range 来建立用户虚拟地址到物理页框的映射。这是整个课程的核心技术点。
2.6 物理内存与 I/O 内存
在嵌入式系统中,物理地址空间不仅包含 DDR RAM,还包含外设寄存器的 I/O 内存区域。mmap 驱动经常需要将这两类物理地址映射到用户空间:
| 类型 | 物理地址范围示例 | 映射方式 |
|---|---|---|
| DDR 内存 | 0x80000000 – 0x8FFFFFFF | 通过 remap_pfn_range 映射 |
| GPIO 寄存器 | 0x11000000 – 0x11000FFF | 通过 ioremap 后映射 |
| 帧缓冲 (Framebuffer) | 0x12000000 – 0x123FFFFF | 通过 dma_alloc_coherent 后映射 |
2.7 本节总结
- 虚拟地址 是进程看到的地址,物理地址 是硬件总线上的真实地址。
- MMU 负责将虚拟地址转换为物理地址,并执行权限和缓存策略。
- 页表 是多级结构,存储虚拟页到物理页框的映射关系。
- 页框 是物理内存的最小管理单元,Linux 默认页大小为 4KB。
- 驱动通过
remap_pfn_range在 mmap 回调中建立用户空间到物理页框的映射。