什么是CMA?
CMA,即Contiguous Memory Allocator,连续内存分配器,顾名思义,就是用来分配连续内存的。 连续内存分配器(CMA - Contiguous Memory Allocator)是一个框架,允许建立一个平台无关的配置,用于连续内存的管理。然后,设备所需内存都根据该配置进行分配。
这个框架的主要作用不是分配内存,而是解析和管理内存配置,以及作为在设备驱动程序和可插拔的分配器之间的中间组件。因此,它是与任何内存分配方法和分配策略没有依赖关系的。
CMA的应用场景有哪些?
一、一些高清视频播放故障时,遇到了内存分配失败的问题
例如:需要大缓冲区的视频编解码器或相机
(e.g., 12 Mega-Pixel Camera, 10 Gbps Network interface card, …)
我们知道,Linux内核中采用虚拟内存技术,应用程序可见的只是线性地址(可以理解为虚拟地址,VA),当应用程序分配连续内存时,得到的是连续的虚拟内存,而其物理内存通常是不连续的,物理内存在缺页时通过buddy system分配和管理,通过页表来建立虚拟地址和物理地址之间的关系,由此可以提升内存利用率、内存管理的效率、而且可以实现进程地址空间的隔离,好处多多。 但在这样的环境中,如果企图分配到一段连续的物理内存,基本是不可能的,应用程序做不了主。但是一些硬件(比如GPU、HDMI)或应用又必须要使用连续的物理内存,原因很多,比如出于性能考虑,此时该怎么办呢?
以前的内存分配的解决办法
通常的做法是在启动时预留一段内存出来专用(内核中有相应的启动参数控制),预留后,这段内存不再由buddy system管理,而通过专用的接口的分配和释放。 如此做法,最大的问题在于“内存浪费”,预留后意味着这段内存将不能再用作他用,预留得多,浪费越多,预留少则担心不够,通常难以抉择和通用,在内存资源紧张的环境中,这样的问题异常严重。
现在的解决方法
GCMA(Guaranteed Contiguous Memory Allocator)
它不仅保证了内存空间效率,而且通过使用 预留技术并让立即丢弃以有效使用该区域来保证快速延迟和成功。我们对 Raspberry Pi 2 的评估显示,与 CMA 相比,在没有系统性能下降的情况下,分配 延迟快 15 到 130 倍,分配延迟更可预测。
CMA介绍
Linux 内核已经提供了一个软件解决方案,称为CMA [13]。基于预订技术的 CMA,应用分页的虚拟内存的基本概念虚拟内存可以移动到物理上的任何其他地方记忆。与预留技术一样,它在引导期间为连续内存分配预留内存。在里面同时,CMA 允许可移动页面驻留在保留的区域以保持内存利用率。如果发出连续的内存位置,则迁移适当的可移动页面从保留区域出来,分配是使用完成的被释放的区域。
为什么不用Scatter/Gather DMA or IOMMU解决分配问题?
Some devices support hardware facilities that are helpfulfor the problem, such as Scatter/Gather DMA or IOMMU [13].Scatter/Gather DMA can gather buffffer from scattered memory regions. IOMMU provides contiguous memory addressspace illusion to devices similar as MMU. However, neitherScatter/Gather CMA nor IOMMU is free. Supporting themrequires additional price and energy(摘自GCMA: Guaranteed Contiguous Memory Allocator)
总结:需要额外的价格和能源,低端设备市场不同于高端市场。由于物联网范式的进步和新兴的低端,这种趋势似乎不会在不久的将来消失智能手机市场。
为什么需要CMA?
解决内存分配和内存使用问题
CMA基本原理
简单的从宏观上描述
跟之前的预留内存的方法类似,CMA也需要“预留内存”,其在内核启动过程中,调用dma_contiguous_reserve
接口预留一段连续的内存区域,该区域的大小由内核配置项:CMA_SIZE_MBYTES
控制,默认为64M,分配后的内存page会被设置上MIGRATE_CMA
标记,表明这些page是给CMA用的(当然不完全,也可以用作他用,这也是CMA的特点)。在free page过程中相应的page也会被加入到free_list中去。
“预留”出来的内存并非只给需要“连续内存”的应用专用,其可以“用作他用”,其他程序也可以通过buddy system分配到CMA区域中的内存,这样就不会造成“内存浪费”,放着不用确实有点可惜了。
应用程序如果需要使用连续的物理内存,需要使用专用接口,如dma_alloc_from_contiguous
,进行申请,过程中,会到之前预留的CMA区域中查找空闲的内存(未被CMA分配出去的),并设置上MIGRATE_ISOLATE
标记,如此buddy system就不会再管理这样的page了,然后,会遍历分配到的page range中是否有已经被buddy system分配“另作他用”的内存,如果有,则需要将这些page迁移出去。 所谓“迁移”,本身上,就是为其找一个新的家:分配新的page,然后将原page拷贝过去,然后将原page释放回CMA中,这样CMA就可以重新利用,并且保证内存连续了。
CMA有什么好处?
CMA相对于之前单纯的保留内存的方法,最大的好处就在于,其解决了“内存浪费”的问题,虽然CMA也需要“保留内存”,但其保留的内存是可以用作他用的,其他应用(包括内核)可以使用其保留内存,当真正需要连续内存的应用调用相应接口分配连续内存时,可以将“用作他用”的这部分内存migrate到其他地方,然后使用为自己腾出连续的空间。 这相当于一个动态内存管理器。 其在一定程度上,即满足了“连续内存”分配的需求,又解决了“空间浪费”的问题,看起来perfect。
CMA有何问题?
问题还是比较明显的:性能问题。 CMA涉及的初衷之一原本是为了为客户提供“连续内存”,从而提升性能的,为何这里性能反而成了问题。 问题还是在于CMA为了避免内存浪费做的“内存迁移”操作,当CMA区域中的内存被用作他用后,如果需要使用已被“用作他用”的这些内存,必然需要内存迁移操作,而该操作必然会有性能损耗,在内存紧张时,尤为明显。在我的环境中,播放高清视频时,其能导致CPU占用率较 高,主要的损耗点就在内存迁移上。 这样的问题,很难平衡,不能不说CMA是个好东西,真的没有一个“皆准”的性能优化手段,性能是跟业务模型、环境息息相关的,根据实际场景,做针对性的“自上而下”(请理解)的剖析和优化,方为“大同”之道。
结论
对于低端嵌入式设备来说,物理上连续的内存分配仍然是一个大问题。例如,Raspberry Pi 是一种流行的 信用卡大小的计算机,尽管内存利用率较低,但仍然使用保留技术,因为 Scatter/Gather DMA 或 IOMMU 等硬件解决方案过于昂贵,而软件解决方案 CMA 则无效。我们介绍了 GCMA,这是一种连续的内存分配器,可确保快速延迟、成功分配和合理的内存空间效率。它通过使用保留技术和有效利用保留区域来实现这些目标。根据我们对 Raspberry Pi 2 的评估,虽然 CMA 在最坏的情况下将在 Raspberry Pi 上拍摄照片从 1 秒增加到 9 秒,但与保留技术相比,GCMA 没有显示额外的延迟。我们的评估还表明,GCMA 不仅优于延迟,而且与 CMA 一 样提高了系统性能。