1. CMA 概述:连续内存分配器的背景与价值

在 Linux 内核中,内存管理子系统长期面临一个核心矛盾:虚拟内存的灵活性与物理内存的连续性之间的冲突。多媒体驱动(Camera、GPU、VPU、Display)以及高性能 DMA 设备,通常要求分配数十 MB 甚至上百 MB 的物理连续大块内存。传统的 Buddy 分配器在系统长期运行后,由于频繁的分配与释放,物理内存页框被分割成大量离散的小块,形成严重的外部碎片,导致即使总空闲内存充足,也无法满足大块连续内存的分配请求。

CMA(Contiguous Memory Allocator)正是为解决这一矛盾而设计的。它允许系统在大部分时间内将预留的连续内存区域用于可移动页面的常规分配,仅在多媒体驱动或 DMA 设备真正需要时,通过页面迁移机制快速回收并整理出所需的连续物理内存块。这种“预留但不独占,按需回收”的设计哲学,使得系统既能享受虚拟内存的碎片容忍能力,又能在关键时刻提供硬件所需的连续物理内存。

核心价值:CMA 将“预留连续内存”与“常规内存复用”统一,避免了为多媒体设备静态预留大量专用内存而导致的浪费,同时解决了长期运行后的内存碎片化问题。

1.1 内存碎片化:多媒体系统的隐形杀手

内存碎片化分为内部碎片和外部碎片。对于多媒体驱动而言,外部碎片是主要威胁。当系统经历长时间的应用程序启动、文件缓存、网络收发后,物理内存的页框分布会变得支离破碎。即使空闲页总数超过 100 MB,Buddy 分配器也可能无法找到一块连续的 8 MB 物理空间给 Camera 的 ISP 硬件使用。

内存碎片化类型对比
碎片类型 产生原因 对多媒体驱动的影响 CMA 的应对
内部碎片 分配粒度大于请求大小(如 Buddy 最小块 4KB) 轻微,浪费少量内存 不直接处理,由 slab/slub 优化
外部碎片 频繁分配释放导致空闲页不连续 致命:无法分配大块 DMA 缓冲区 通过页面迁移整理出连续区域
经验数据:在 4GB RAM 的嵌入式设备上,连续运行 72 小时后,Buddy 分配器最大连续块通常从启动时的 512 MB 下降到不足 4 MB。此时若不加 CMA,4K 视频编码所需的 16 MB 连续内存将直接分配失败。

1.2 CMA 在多媒体驱动中的关键作用

多媒体子系统是 CMA 最典型、最迫切的使用场景。以下设备通常依赖 CMA 提供连续物理内存:

  • Camera (V4L2/ISP): 图像传感器输出 RAW 数据需要大块连续缓冲区用于 ISP 处理,通常 4~32 MB。
  • GPU/Display: 帧缓冲(Framebuffer)和显示控制器要求物理连续内存,避免扫描输出时出现撕裂。
  • Video Codec (VPU): 硬件编解码器需要连续内存存放参考帧和比特流缓冲区,4K/8K 场景下可达 64 MB。
  • Audio DSP: 低延迟音频处理需要连续的 DMA 缓冲区,避免因 SG(Scatter-Gather)带来的额外延迟。
  • Network Accelerator: 部分网卡硬件不支持 SG 模式,需要连续内存存放描述符和数据包。
CMA 在多媒体驱动中的核心位置 CMA 在多媒体驱动中的核心位置 CMA 核心 连续内存分配器 多媒体应用 Camera ISP GPU/Display Video Codec Audio DSP CMA 在多媒体栈中承上启下:为上层应用提供透明连续内存,为底层硬件提供物理连续 DMA 缓冲区

1.3 CMA 与传统方案的对比优势

在 CMA 出现之前,嵌入式系统通常采用以下方案解决连续内存问题,但各有明显缺陷:

连续内存分配方案对比
方案 实现方式 优点 缺点
静态预留 在 bootargs 中预留大块内存,驱动独占 实现简单,分配可靠 内存浪费严重,系统可用内存减少
IOMMU/SMMU 硬件 IOMMU 将离散物理页映射为连续 IOVA 无需物理连续,灵活性高 硬件成本高,增加 TLB 压力,延迟增加
Scatter-Gather DMA DMA 引擎支持链表描述符,处理非连续内存 无需连续内存 驱动复杂度高,部分硬件不支持,性能下降
CMA 预留区域 + 页面迁移 + 按需回收 兼顾内存利用率与连续性,透明 分配时可能触发页面迁移延迟
注意事项:CMA 并非万能。在实时性要求极高的场景(如 ISR 中分配),CMA 的页面迁移可能导致不可接受的延迟。此时需要结合预分配或 DMA 缓冲池技术。此外,CMA 区域的大小需要在系统启动时通过设备树或内核参数指定,过大则浪费内存,过小则无法满足峰值需求。

1.4 CMA 的工作流程概览

CMA 的完整生命周期包含以下关键步骤,理解这些步骤是后续章节深入分析的基础:

  1. 预留阶段:系统启动时,通过设备树 reserved-memory 节点或内核参数 cma= 预留一段物理连续内存区域。
  2. 注册阶段:CMA 区域注册到内核的 CMA 框架中,关联对应的 cma 结构体。
  3. 常规复用:在无多媒体分配请求时,CMA 区域内的页面被加入 Buddy 系统的 MIGRATE_CMA 迁移类型中,供可移动页面使用。
  4. 分配触发:多媒体驱动调用 dma_alloc_coherent() 或 cma_alloc(),请求连续物理内存。
  5. 页面迁移:若 CMA 区域内存在非可移动页面,内核通过 alloc_contig_range() 触发页面迁移,将页面逐页移出 CMA 区域。
  6. 分配完成:迁移完成后,CMA 区域变为连续空闲状态,分配器返回物理连续的内存给驱动。
  7. 释放回收:驱动使用完毕后调用 dma_free_coherent() 或 cma_release(),页面重新回到 CMA 区域,再次可供常规分配。
CMA 工作流程概览 CMA 工作流程概览 1. 预留区域 2. 注册 CMA 3. 常规复用 4. 分配触发 5. 页面迁移 6. 分配完成 7. 释放回收 关键:步骤 3 和步骤 5 体现了 CMA “预留但不独占,按需回收” 的核心设计哲学 页面迁移是 CMA 性能的关键瓶颈,后续章节将深入分析其实现与优化

1.5 本章小结

CMA 是 Linux 内核中解决物理内存碎片化与大块连续内存需求之间矛盾的关键基础设施。它通过预留区域 + 页面迁移的机制,在保证内存利用率的同时,为多媒体驱动和 DMA 设备提供了可靠的连续内存分配能力。理解 CMA 的背景、解决的问题以及在多媒体栈中的核心位置,是后续深入学习其实现原理、配置优化和驱动集成的基础。

关键概念回顾:
  • 外部碎片是多媒体系统连续内存分配失败的主要原因。
  • CMA 通过 MIGRATE_CMA 迁移类型实现常规内存复用。
  • 页面迁移是 CMA 分配的核心步骤,也是性能关键点。
  • CMA 适用于 Camera、GPU、VPU、Display、Audio DSP 等多媒体设备。