嵌入式内存优化与CMA连续分配低内存适配实战工程 - 第2节

2. Linux内核内存管理基础

在嵌入式 Linux 系统中,每个进程都拥有独立的 虚拟地址空间(Virtual Address Space),而实际的存储介质是有限的 物理内存(Physical Memory)。虚拟内存机制让进程误以为自己独占整个内存空间,从而简化编程模型,同时通过页表实现地址转换与隔离。

2.1.1 核心概念对比

概念 定义 嵌入式关键点
虚拟内存 CPU 发出的地址,经过 MMU 转换后访问物理内存 每个进程 32 位下 4GB 空间(3GB 用户 + 1GB 内核)
物理内存 DRAM/SRAM 芯片上的实际存储单元 嵌入式通常 64MB~2GB,需高效利用
页表 虚拟页号 → 物理页框号的映射表 ARMv7 使用二级页表,ARMv8 使用多级页表
MMU 内存管理单元,硬件执行地址转换 可关闭(实模式)或开启(保护模式)
嵌入式实战提示: 在低内存设备(如 256MB RAM)上,虚拟内存的页表本身会占用物理内存。每页 4KB 时,4GB 虚拟空间需要约 4MB 页表空间。使用 大页(HugeTLB) 或 LPAE 可减少页表开销。

2.1.2 地址转换流程 SVG

虚拟地址 → 物理地址转换流程 虚拟地址 0x0001_2345 MMU 页表查找 物理地址 0xA000_2345 TLB (快表) 说明 虚拟地址通过 MMU 查询页表/TLB 转换为物理地址

2.2 页表结构与多级映射

页表是虚拟内存的核心数据结构。ARM 架构下,嵌入式 Linux 通常使用 二级页表(Section/Page 映射)或 三级页表(LPAE 扩展)。页表项(PTE)包含物理页框号、权限位、脏位、访问位等控制信息。

2.2.1 ARMv7 二级页表结构

级别 名称 粒度 覆盖范围 页表项数
L1 页全局目录 (PGD) 1MB (Section) 4GB 4096
L2 页表 (PTE) 4KB (Page) 1MB 256
关键指标: 嵌入式设备中,内核通常只映射低端物理内存(如 512MB),高端内存通过 vmalloc 或 kmap 临时映射。这减少了页表占用,但也带来了连续物理内存分配的限制——这正是 CMA 要解决的问题。

2.2.2 多级页表查找流程 SVG

ARMv7 二级页表查找过程 虚拟地址: 0x0001_2345 L1索引(12bit) | L2索引(8bit) | 页内偏移(12bit) L1 页表 (PGD) 4096 项 × 4B = 16KB L2 页表 (PTE) 256 项 × 4B = 1KB 物理页 页内偏移 0x345 → 物理页基址 + 0x345 嵌入式优化点 • 使用 Section 映射(1MB 粒度)可跳过 L2 页表,减少 TLB 未命中 • 内核线性映射区(低端内存)使用 Section 映射,性能更高

2.3 MMU 工作原理与 TLB

MMU(Memory Management Unit)是 CPU 内部的硬件模块,负责将虚拟地址转换为物理地址。每次内存访问都经过 MMU,因此其效率直接影响系统性能。TLB(Translation Lookaside Buffer)是 MMU 内部的页表缓存,用于加速地址转换。

2.3.1 MMU 核心功能

  • 地址转换: 虚拟地址 → 物理地址,支持段页式映射
  • 权限检查: 读/写/执行权限,内核态/用户态隔离
  • 缓存控制: 页表项中的 C/B 位控制是否缓存、写策略(write-back/write-through)
  • 异常处理: 缺页异常(Page Fault)触发内核分配物理页

2.3.2 TLB 未命中处理流程 SVG

TLB 未命中处理流程 CPU 访问 VA TLB 命中? 直接访问 PA 硬件页表遍历 PTE 有效? 更新 TLB 缺页异常 → 内核
嵌入式陷阱: 在低内存设备上,频繁的 TLB 未命中会导致严重的性能下降。建议:① 使用 Section 映射(1MB 粒度)减少页表项;② 开启 TLB 预取(如 ARM 的 TLB maintenance 广播);③ 避免过多的 vmalloc 映射碎片。

2.4 内核内存布局与低内存适配

嵌入式 Linux 内核将虚拟地址空间划分为 用户空间 和 内核空间。在 32 位系统中,典型的划分是 3GB/1GB 或 2GB/2GB。内核空间进一步分为 直接映射区(低端内存)、vmalloc 区、固定映射区 和 高端内存映射区。

2.4.1 内核空间布局(以 ARM 为例)

区域 虚拟地址范围 用途 物理连续性
直接映射区 0xC000_0000 ~ 0xF000_0000 线性映射物理内存(低端内存) 连续
vmalloc 区 0xF000_0000 ~ 0xFE00_0000 非连续内存分配(模块、I/O 映射) 非连续
固定映射区 0xFE00_0000 ~ 0xFF00_0000 固定虚拟地址映射(如中断向量) 按需
高端内存映射区 0xFF00_0000 ~ 0xFFFF_FFFF 临时映射高端物理内存 非连续

2.4.2 低内存设备的内存压力 SVG

低内存设备的内存压力与 CMA 切入点 物理内存 (256MB) 内核占用 (64MB) 应用程序 + 匿名页 (120MB) 空闲 (72MB) 问题:大块连续分配失败 如 Camera 需要 8MB 连续物理内存 CMA 预留区:从空闲区划出 16MB 可移动页面迁移后,提供连续物理内存
CMA 的切入点: 从上图可见,物理内存碎片化导致大块连续分配失败。CMA(Contiguous Memory Allocator)在系统启动时预留一块物理连续区域,平时允许可移动页面使用,当驱动需要连续内存时,通过页面迁移腾出空间。这既保证了内存利用率,又满足了连续分配需求。

2.5 本章小结

  • 虚拟内存 为每个进程提供独立地址空间,通过 MMU 和 页表 实现地址转换
  • 页表 采用多级结构(ARMv7 二级,ARMv8 三级),嵌入式设备可通过 Section 映射减少开销
  • TLB 是 MMU 内部的页表缓存,未命中时触发硬件页表遍历或缺页异常
  • 内核空间 分为直接映射区、vmalloc 区等,低端内存连续映射,高端内存需临时映射
  • 低内存设备 面临物理内存碎片化问题,CMA 通过预留 + 页面迁移机制提供连续分配能力