内存管理@CMA
CMA的全称是 Contiguous Memory Allocator,于2011年导入,用于分配大块的、连续的内存。
对于常规开发人员来说,无需关注这一变化,因为 CMA集成在了原有的 DMA模块中,所以之前的 DMA API(例如,dma_alloc_coherent())仍然可以使用。实际上,常规开发人员完成不需要关注或者说直接调用 CMA API,因为它是基于 page/page frame numbers(PFNs)开发的,而在 bus地址和 kernel映射上未作任务更改。
申请 CMA内存,只需要调用:
struct page *dma_alloc_from_contiguous(struct device *dev, int count, unsigned int align);
释放:
bool dma_release_from_contiguous(struct device *dev, struct page *pages, int count);
想要进一步了解 CMA的工作原理,我们需要了解一点页的“迁移属性(migrate type)”。
我们从伙伴系统申请内存时,需要提供 gfp_mask,用于指定需要分析的内存类型。而这个 gfp_mask就指定了所要申请页的“迁移属性”,然后系统就根据这些请求,从对应的内存类型中分配内存页。
但也有可能有异常,比如所要求的页不够用了,系统就会试图从别外的内存类型中分配,同时把该内存页的属性更改。这就意味着,“不可移动页”可能是从 "可迁移(MIGRATE_MOVABLE)"类型的内存中分配的,而且这些被分配的内存页的属性也会被更改成“不可移动”,那这就会直接导致连续的内存被这些“不可移动页”分割,而由于其“不可移动”性,后面再要申请连续的大块内存时就无法分配了。
所以,在开机初始化时,通过调用 dma_contiguous_reserve() / dma_delacre_contiguous(),系统就为 CMA预留了一片连续的内存空间,后面 CMA再将这些内存空间的迁移属性标记为 MIGRATE_CMA,再还给内存伙伴系统,然后这片内存区域就还是可以供“可移动”的页使用,但不用再担心被“不可迁移”的页所分割,而后续当需要分配大量的连续空间时,系统将把这些“可迁移”的内容迁移到别的页上,再将内存分配出来。