1. CMA 概述:背景、设计目标与在 Linux 内核中的地位

1.1 为什么需要 CMA?—— 连续内存的困境

在嵌入式 Linux 系统中,内存碎片化是一个长期存在的工程难题。系统经过长时间运行后,物理内存被分割成大量不连续的小块,虽然总空闲内存充足,但很难分配出一块大的连续物理内存。这对于需要大块连续缓冲区的硬件模块(如摄像头传感器、GPU、显示控制器、视频编解码器)来说,是致命的。

传统解决方案存在明显缺陷:

  • 预留法(reserved memory):在系统启动时静态预留一大块连续内存,专供某个驱动使用。缺点是内存利用率极低,预留区域在空闲时也无法被其他子系统使用。
  • 运行时分配(如 __get_free_pages):在需要时尝试分配连续物理页。缺点是随着系统运行时间增加,高阶分配(order > 3)的成功率急剧下降,容易导致分配失败。
核心矛盾:硬件设备需要大块连续物理内存,而通用内存管理(页分配器)倾向于保持内存碎片化以提高灵活性。CMA 正是为解决这一矛盾而设计的。

1.2 CMA 的设计目标

CMA(Contiguous Memory Allocator)是 Linux 内核提供的一种内存管理机制,旨在兼顾连续内存的可用性与系统整体内存利用率。其设计目标可归纳为以下四点:

设计目标 说明 工程价值
按需分配连续内存 仅在驱动真正需要时,从 CMA 区域分配连续物理页 避免静态预留带来的浪费
空闲时回收利用 CMA 区域中的空闲内存可被可移动页面(movable pages)占用 提升整体内存利用率
可迁移性保证 当驱动申请连续内存时,内核能迁移走 CMA 区域中的可移动页面 保证分配成功率
与伙伴系统协同 CMA 区域作为伙伴系统的特殊内存区域,参与正常的页管理 降低实现复杂度,复用现有机制

1.3 CMA 在 Linux 内核中的位置

CMA 位于 Linux 内核内存管理子系统的中间层,向上服务于驱动框架,向下依赖于伙伴系统和页迁移机制。它不是独立的内存分配器,而是对伙伴系统的扩展和补充。

架构定位:CMA 属于 mm/cma.c 和 mm/cma.h 核心实现,与 mm/page_alloc.c(伙伴系统)、mm/migrate.c(页迁移)紧密耦合。设备驱动通过 dma_alloc_coherent 或 cma_alloc 接口间接使用 CMA。
CMA 在 Linux 内核内存管理架构中的位置 设备驱动层(Camera / GPU / VPU / Display) DMA API / cma_alloc / cma_release CMA 核心(mm/cma.c) 伙伴系统(page_alloc.c) 页迁移(migrate.c) 内存回收(vmscan.c) 物理内存(CMA 区域 + 普通区域 + 预留区域) 说明:CMA 核心依赖伙伴系统管理页块,依赖页迁移腾出连续空间,依赖内存回收触发页面换出

1.4 CMA 与伙伴系统的关系

CMA 区域在伙伴系统中被标记为 MIGRATE_CMA 迁移类型。这是理解 CMA 行为的关键:

  • 普通分配:伙伴系统在分配可移动页面时,可以优先从 CMA 区域分配,从而提高 CMA 区域的内存利用率。
  • CMA 分配:当驱动调用 cma_alloc 时,内核会扫描 CMA 区域,如果目标页块被可移动页面占用,则通过页迁移机制将其移走,然后返回连续的物理页。
  • 不可抢占:不可移动页面(如内核核心代码、页表)不能分配到 CMA 区域,否则会导致 CMA 分配失败。
工程陷阱:如果驱动在 CMA 区域中分配的连续内存被长期持有(如视频环型缓冲),会导致该区域的可移动页面被驱逐,降低内存利用率。设计驱动时应尽量缩短 CMA 缓冲区的持有时间,或使用缓冲池复用机制。

1.5 CMA 的典型使用场景

场景 连续内存需求 CMA 优势
摄像头 ISP 处理 4~16 MB 连续缓冲区 避免预留大块内存,空闲时可被文件缓存使用
GPU 帧缓冲 8~32 MB 连续显存 支持多进程共享 CMA 区域,减少显存浪费
视频编解码器 2~8 MB 连续参考帧缓存 按需分配,编解码结束后立即释放回 CMA
显示控制器(DSI/LVDS) 1~4 MB 连续显示缓冲 支持双缓冲切换,空闲区域可被其他模块复用

1.6 CMA 的局限性

尽管 CMA 解决了连续内存分配的核心矛盾,但它并非银弹。在实际工程中需要注意以下限制:

  • 分配延迟不可控:当 CMA 区域被可移动页面占满时,cma_alloc 需要触发页迁移和回收,可能导致毫秒级延迟,不适合硬实时场景。
  • 区域大小需提前配置:CMA 区域的大小和位置在系统启动时通过设备树或内核参数指定,无法动态调整。
  • 碎片化累积:频繁的分配和释放可能导致 CMA 区域内部碎片化,虽然伙伴系统会尽力合并,但极端情况下仍可能分配失败。
  • 不支持 DMA 不连续设备:部分硬件支持分散-聚集 DMA(SG-DMA),此时不需要 CMA,使用页分配器即可。
工程建议:在系统设计阶段,应根据设备的最大连续内存需求、分配频率和实时性要求,合理规划 CMA 区域的大小和数量。对于实时性要求高的场景,可考虑使用预留内存(reserved memory)替代 CMA。

1.7 本章小结