4. Camera内存分配策略:连续内存分配、分散内存分配、ION内存管理器
做Camera驱动开发,内存管理这块是绕不开的坎。我刚开始接触时也踩过不少坑,尤其是不同平台、不同分辨率下,选错分配策略,轻则掉帧,重则直接黑屏。今天咱们就把这块掰开揉碎了聊。
4.1 连续内存分配:简单粗暴,但有限制
连续内存分配,说白了就是给Camera sensor要一块物理地址连续的大内存。为什么需要连续?因为很多老旧的ISP硬件或者DMA控制器,它只认连续的物理地址,你给它分散的,它直接罢工。
优点很明显:
- 硬件兼容性好,几乎所有DMA引擎都支持
- 地址转换开销小,CPU访问也快
- 实现简单,内核里dma_alloc_coherent一把梭
缺点也致命:
- 系统运行久了,内存碎片化严重,大块连续内存很难申请到
- 浪费严重,比如你只需要3MB,但系统可能给你对齐到4MB
- 在嵌入式设备上,连续内存总量有限,多路Camera同时跑就捉襟见肘
核心代码示例:
// 分配连续内存
dma_addr_t dma_phys;
void *cpu_addr = dma_alloc_coherent(dev, size, &dma_phys, GFP_KERNEL);
if (!cpu_addr) {
printk(KERN_ERR "Failed to allocate coherent DMA memory\n");
return -ENOMEM;
}
// 使用完后释放
dma_free_coherent(dev, size, cpu_addr, dma_phys);
我的经验: 在项目初期,如果Camera分辨率不超过1080p,用连续分配完全够用。但一旦上到4K甚至8K,连续分配基本就是噩梦。我曾经在一个项目里,系统跑了12小时后,连一块2MB的连续内存都申请不到,最后只能重启。嗯,从那以后我学乖了。
4.2 分散内存分配:灵活但管理复杂
分散内存分配,也叫SG(Scatter-Gather)模式。它允许DMA访问不连续的物理内存块,每个块通过一个SG表来描述。说白了,就是把一堆零散的小内存拼起来用。
为什么需要它?
- 系统内存碎片化严重时,分散分配几乎总能成功
- 内存利用率高,按需分配,不浪费
- 适合大分辨率、多路Camera的场景
代价是什么?
- 硬件必须支持SG模式,老旧的DMA控制器不行
- SG表的构建和维护有额外开销
- 驱动代码复杂度上升,你得自己管理SG列表
核心代码示例:
struct scatterlist *sg;
int nents;
// 分配分散内存
sg = kmalloc(sizeof(struct scatterlist) * num_pages, GFP_KERNEL);
sg_init_table(sg, num_pages);
for (i = 0; i < num_pages; i++) {
struct page *page = alloc_page(GFP_KERNEL | __GFP_ZERO);
sg_set_page(&sg[i], page, PAGE_SIZE, 0);
}
// 映射给DMA使用
nents = dma_map_sg(dev, sg, num_pages, DMA_FROM_DEVICE);
// 使用完后解除映射并释放
dma_unmap_sg(dev, sg, nents, DMA_FROM_DEVICE);
for (i = 0; i < num_pages; i++) {
__free_page(sg_page(&sg[i]));
}
kfree(sg);
注意: 分散分配虽然灵活,但每次DMA传输时,硬件需要遍历SG表。如果SG表项太多(比如几百个),传输效率会下降。我建议单个SG表项不要超过256个,否则性能损耗明显。
4.3 ION内存管理器:Android生态的标配
说到Camera内存管理,ION是绕不开的。它是Android系统里专门为多媒体场景设计的内存分配器。你想想看,Camera、GPU、Display、Video编解码,这些模块之间需要共享内存,如果各管各的,那不乱套了?
ION的核心概念:
- Heap(堆):不同类型的物理内存池,比如连续堆、系统堆、Carveout堆
- Buffer(缓冲区):分配出来的内存块,可以跨进程共享
- Handle(句柄):用户空间操作Buffer的凭证
- FD(文件描述符):通过fd实现跨进程传递
常见的ION Heap类型:
| Heap类型 | 特点 | 适用场景 |
|---|---|---|
| ION_HEAP_TYPE_SYSTEM | 从系统内存分配,非连续 | 小分辨率、低帧率 |
| ION_HEAP_TYPE_SYSTEM_CONTIG | 从系统内存分配,但保证连续 | 需要连续内存的ISP |
| ION_HEAP_TYPE_CARVEOUT | 从预留的固定区域分配,连续 | 高分辨率、高帧率 |
| ION_HEAP_TYPE_CHUNK | 从大块连续内存中切分 | 多路Camera同时工作 |
ION分配示例(用户空间):
#include <linux/ion.h>
#include <linux/ion_4.12.h>
int ion_fd = open("/dev/ion", O_RDWR);
struct ion_allocation_data alloc_data;
alloc_data.len = buffer_size;
alloc_data.heap_id_mask = 1 << ION_HEAP_TYPE_SYSTEM_CONTIG;
alloc_data.flags = 0;
// 分配ION buffer
ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data);
// 获取fd用于跨进程共享
struct ion_fd_data fd_data;
fd_data.handle = alloc_data.handle;
ioctl(ion_fd, ION_IOC_SHARE, &fd_data);
// 现在可以通过fd_data.fd来操作这块内存了
// 使用完后关闭
close(fd_data.fd);
close(ion_fd);
我的建议: 在Android平台上做Camera驱动,优先使用ION。它帮你解决了跨进程共享、内存复用、缓存同步等一堆麻烦事。我曾经在一个项目里自己手写内存池,结果发现ION早就把这些问题都处理好了。别重复造轮子。
4.4 如何选择?实战中的决策依据
说了这么多,到底该用哪种?我一般按这个思路来决策:
- 看硬件能力:DMA控制器是否支持SG模式?不支持的话,只能连续分配。
- 看分辨率:1080p以下,连续分配够用;4K以上,必须用分散或ION。
- 看平台:Android平台无脑选ION;纯Linux平台,看内核版本,4.19以上推荐DMA-BUF。
- 看内存压力:系统内存紧张时,分散分配比连续分配更可靠。
避坑指南: 我曾经在一个项目里,为了省事全部用连续分配。结果产品在用户手里跑了几天后,Camera突然打不开。查了半天,是内存碎片导致连续内存申请失败。从那以后,我只要分辨率超过1080p,一律用ION的SYSTEM_CONTIG或者直接上分散分配。
嗯,内存分配策略这块,说白了就是权衡。没有银弹,只有最适合你当前场景的方案。下一节咱们聊聊DMA传输的优化技巧,到时候会用到今天讲的内存分配知识。