Linux 内存预留指南
内存预留概览
Linux 内核的内存预留可以大致分为两类:
- no-map:被预留的内存完全从内核伙伴系统中移除,内核在正常运行时无法访问或使用这部分内存,通常不会为其建立虚拟地址映射。这块内存对内核来说是“不可见”的,隔离性最强。
- CMA:被预留的内存名义上属于某个用途,但在其未被使用时,可以被内核借用给普通的可移动页使用。
no-map
内核启动参数预留
方法
在 bootloader 传递给内核的命令行参数中,使用 memmap=<size>$<address>。
示例
# 从物理地址 0x50000000 开始,预留 64MB 内存
bootargs=... memmap=64M$0x50000000
设备树预留
示例
reserved-memory {
/* ... */
// 1. 定义一个带标签的 no-map 区域
dsp_mem: dsp_memory_region@30000000 {
reg = <0x0 0x30000000 0x0 0x1000000>; // 16MB
no-map;
};
};
// ...
// 2. 在 DSP 设备节点中引用这个区域
dsp_controller: dsp@C088C000 {
compatible = "vendor,dsp-controller";
/* ... */
// Linux 驱动通过读取此属性,得知 DSP 可用的物理内存地址和大小
memory-region = <&dsp_mem>;
};
CMA
内核启动参数预留
方法
- 在 bootloader 传递给内核的命令行参数中,使用
cma=<size>[@<address>] - 示例
# 创建一个 512MB 大小的全局 CMA 区域
bootargs=... cma=512M
配置内核预留
方法
在 menuconfig 中设置 CONFIG_CMA_SIZE_MBYTES 的值。
示例
//in kernel .config
CONFIG_DMA_CMA=y
CONFIG_CMA_SIZE_SEL_MBYTES=y
CONFIG_CMA_SIZE_MBYTES=256 // 设置一个 256MB 的全局 CMA 区域
设备树预留
全局预留
方法
设备树中带有 linux,cma-default 属性的节点。这是通过设备树定义全局池的标准方法。
注意,全局 CMA 池只能有一个,优先顺序是DTS > CMDLINE > DEFCONFIG,即 如果dts有合法的节点,会
以dts节点配置为最终参数,否则看cmdline是否有cma=size@base,最后才看defconfig是否有定义。
示例
reserved-memory {
/* ... */
linux,cma {
compatible = "shared-dma-pool";
reusable;
size = <0x10000000>; // 256MB
alignment = <0x2000>;
linux,cma-default; // <-- 此属性将其标记为全局默认池
};
};
设备独占预留
方法
- 第一步:在 reserved-memory 中定义一个带标签的 CMA 区域。这个节点不能有 linux,cma-default 属性。
- 第二步:在具体的设备节点中,通过 memory-region 属性引用该标签。
示例
reserved-memory {
/* ... */
vpu_cma_pool: vpu_cma@90000000 { // <-- "vpu_cma_pool" 是标签
compatible = "shared-dma-pool";
reusable;
reg = <0 0x90000000 0 0x8000000>; // 128MB
};
};
vpu_node: vpu@12340000 {
compatible = "vendor,vpu-driver";
/* ... */
memory-region = <&vpu_cma_pool>; // <-- 引用上面定义的独占池
};
通过这种方式,vpu_node 的驱动在申请连续内存时,内核会定向到 vpu_cma_pool 这个专用的 128MB 池中进行分配,而不会使用全局的 CMA 池。