【Android车载学习笔记】第七天:DMA和QMEM介绍
2026/7/1 10:35:25 网站建设 项目流程

QMEM与DMA底层原理

在高通SA8295智驾QNX系统中,开发QCarCam多路摄像头、ADAS视觉算法时,一定会接触两个底层核心模块:DMA硬件传输控制器PMEM物理内存分配器

二者是车载图像采集链路的底层基石,缺一不可:DMA负责硬件高速搬运图像像素,PMEM负责提供DMA硬件可识别的连续物理内存。

很多开发新手会混淆二者职责,出现黑屏、花屏、丢帧、内核崩溃等问题。本文结合QCarCam相机采集场景,从原理、协作流程、API、踩坑点完整讲解PMEM与DMA。

DMA:直接内存访问(Direct Memory Access)

1. 核心定义

DMA是SoC内置独立硬件控制器,核心能力:外设硬件绕开CPU,直接和物理内存双向大批量传输数据
传统数据传输逻辑(无DMA):
摄像头硬件 → CPU寄存器拷贝 → 内存
CPU全程占用,1080P图像单帧3MB数据拷贝会严重占用算力,多路摄像头场景直接掉帧、算法卡顿。

DMA传输逻辑:
摄像头CSI硬件 ↔ 物理内存 直连传输
CPU仅下发一次传输指令,后续数据搬运完全由DMA硬件独立完成,传输完成仅通过中断通知CPU,CPU可并行运行业务、视觉算法。

2. DMA三大核心优势(车载摄像头刚需)

  1. 极低CPU占用
    6路GMSL3摄像头同时输出图像时,海量像素数据完全由DMA搬运,CPU算力全部留给AVM拼接、障碍物识别等业务。
  2. 微秒级低延迟
    省去CPU中转拷贝流程,Sensor采集到图像到内存可读仅微秒级,满足自动驾驶实时性要求。
  3. 超大并行带宽
    SA8295内置多路独立DMA通道,CSI、ISP、GPU、GMSL解串器可同时并行传输数据,互不抢占资源。

3. 车载场景下DMA的硬性限制

DMA硬件只能识别物理地址连续的内存块,无法使用普通malloc分配的碎片化虚拟内存:

  1. malloc分配的是虚拟内存,物理地址零散分段;
  2. DMA控制器没有虚拟内存地址翻译单元,传入虚拟地址会触发硬件访问异常、系统崩溃;
  3. 图像、雷达等大块数据,必须使用专用内存分配器申请连续物理内存,在QNX平台该分配器就是PMEM。

4. QCarCam链路中DMA的工作节点

  1. GMSL解串器DMA:远距离摄像头图像通过GMSL线缆传入SOC,DMA将像素写入物理内存;
  2. CSI DMA:将解串器输出图像搬运至PMEM图像缓冲区;
  3. ISP DMA:硬件完成降噪、畸变矫正、HDR处理,在多块PMEM Buffer之间搬运图像;
  4. 显示DMA:AVM拼接完成后,直接将画面从PMEM缓冲区刷写至车载显示屏。

PMEM:QNX平台专用物理内存管理器(pmem.h)

1. PMEM是什么

PMEM全称Physical Memory,是高通为SA8295 QNX智驾系统定制的内存管理组件,配套头文件pmem.h、运行库libpmem.so

Android座舱平台使用ION内存实现同类功能,QNX无ION,全部依赖PMEM

核心定位:专门分配物理地址连续、支持DMA硬件访问的大块内存,为CSI、ISP、GPU等DMA外设提供合规缓冲区。

2. PMEM核心能力(pmem.h对外API)

(1)内存分配与释放
// 分配指定大小连续物理内存,flags控制缓存属性、共享属性pmem_handle_tpmem_alloc(size_tsize,uint32_tflags);// 回收内存,释放物理地址资源intpmem_free(pmem_handle_thndl);

分配关键Flag:

  • PMEM_FLAG_NOCACHE:无缓存内存,图像采集必选,解决CPU与DMA缓存数据不一致;
  • PMEM_FLAG_CONTIGUOUS:强制物理地址连续,DMA硬件强制要求。
(2)虚拟地址/物理地址转换
// 获取硬件DMA使用的物理地址(传给CSI DMA寄存器)uint64_tpmem_get_phys(pmem_handle_thndl);// 获取用户态虚拟地址(CPU读取图像像素数据)void*pmem_get_virt(pmem_handle_thndl);

对应QCarCamqcarcam_plane_t结构体两个核心字段:
plane->phys_addr= pmem_get_phys()
plane->virt_addr= pmem_get_virt()

(3)缓存同步接口(解决花屏核心API)
// CPU修改内存后,刷新缓存至物理内存,DMA硬件才能读取新数据voidpmem_flush(pmem_handle_thndl);// DMA硬件写入图像后,失效CPU缓存,CPU读取最新图像帧voidpmem_invalidate(pmem_handle_thndl);
(4)跨进程内存共享
// 将PMEM内存导出为fd,可传递给算法、显示进程intpmem_export_fd(pmem_handle_thndl);// 其他进程通过fd导入同一块物理内存,实现图像帧共享pmem_handle_tpmem_import_fd(intfd);

多路AVM多进程协作、相机进程向算法进程传递图像帧依赖该接口。

3. PMEM与普通malloc的核心区别

特性pmem_allocmalloc
物理地址强制连续碎片化、不连续
DMA兼容可直接传入CSI/ISP DMADMA硬件无法识别,会崩溃
缓存可控支持无缓存、写合并配置默认开启CPU缓存,无法调整
跨进程共享原生支持fd导出共享仅本进程可用,无法跨进程传递
适用场景相机帧、ISP、GPU、雷达大块硬件缓冲区普通程序小数据、业务变量

PMEM与DMA完整协作流程(QCarCam相机采集场景)

结合SA8295 QNX平台,完整一帧图像从摄像头到应用读取全链路,清晰展示PMEM与DMA的配合关系:

  1. 应用层配置Buffer
    调用qcarcam_s_buffers()设置分辨率、buffer数量,libqcarcam底层自动调用pmem_alloc批量分配多块连续无缓存物理内存。
  2. 获取DMA可用物理地址
    libqcarcam通过pmem_get_phys()取出每块Buffer的物理地址,下发至CSI DMA控制器寄存器。
  3. DMA硬件传输图像(全程无CPU参与)
    GMSL解串器接收摄像头像素数据,CSI DMA控制器直接将像素写入PMEM分配的物理内存块。
  4. 传输完成中断通知CPU
    DMA写完一帧图像后,触发硬件中断通知libqcarcam,标记该帧为就绪状态。
  5. 应用读取图像数据
    调用qcarcam_get_frame()获取帧结构体,通过virt_addr访问像素;读取前必须调用pmem_invalidate()失效CPU缓存,否则读取旧画面、花屏。
  6. 帧归还缓冲池
    图像处理完成后调用qcarcam_release_frame(),Buffer归还DMA缓冲池,等待下一帧DMA写入;进程退出时libqcarcam统一调用pmem_free回收全部物理内存。

Android ION内存

高通Android IVI座舱(SA8155/SA8295 Android)完全采用ION(I/O Memory Manager)作为DMA配套内存管理器,替代QNX的PMEM。

ION是Android原生标准化内存组件,专为Camera、ISP、GPU、显示等需要连续物理内存+DMA零拷贝的多媒体硬件设计,是QCarCam座舱版采集图像Buffer的底层依赖。
本文结合座舱QCarCam开发场景,完整讲解ION原理、API、和DMA协作、编译配置、与PMEM对比、高频踩坑。

ION核心定位与诞生背景

1. 什么是ION

ION是Google从Android 4.0引入的统一物理内存分配框架,统一管理各类硬件DMA缓冲区,解决早期碎片化PMEM、ashmem内存管理混乱问题。
核心职责:

  1. 分配DMA硬件可用的连续物理内存(CSI/ISP/GPU硬性要求);
  2. 基于dma-buf实现跨进程、跨硬件零拷贝共享
  3. 统一管理CPU Cache缓存同步,解决DMA与CPU数据不一致;
  4. 提供多类型内存堆(Heap),适配摄像头、显示、AI等不同硬件需求。

2. Android座舱为什么必须用ION,不能用malloc

  1. malloc分配虚拟内存,物理地址碎片化,DMA控制器无MMU地址翻译,无法识别;
  2. 多路GMSL摄像头、ISP流水线需要超大块连续物理帧缓存,普通堆内存无法稳定分配;
  3. AVM环视、DMS多进程需要共享图像帧,ION通过fd实现无拷贝内存传递;
  4. 支持IOMMU地址重映射、无缓存/写合并缓存策略,适配图像采集实时性需求。

3. ION与DMA底层依存关系

DMA硬件(CSI/ISP)只能读写连续物理内存,ION唯一作用就是给DMA提供合规内存块:

  1. ION分配物理连续Buffer → 导出物理地址给DMA寄存器;
  2. DMA直接将摄像头像素写入ION物理内存,全程不经过CPU;
  3. 用户态通过mmap映射ION内存虚拟地址,CPU读取图像像素;
  4. 依靠ION缓存同步API,解决CPU Cache与硬件DMA内存数据不同步(花屏根源)。

ION四大核心Heap堆

内核启动时预先划分多块独立内存池,不同硬件业务选择对应Heap分配,高通座舱Camera固定使用ION_HEAP_TYPE_MULTIMEDIA / ION_HEAP_TYPE_IOMMU

Heap类型用途QCarCam场景是否使用
ION_HEAP_TYPE_CARVEOUT系统预留大块静态物理内存(老式PMEM替代方案)极少用
ION_HEAP_TYPE_SYSTEM_CONTIG小块连续物理内存小参数缓存
ION_HEAP_TYPE_SYSTEM虚拟连续、物理碎片内存,不支持DMA相机禁用
ION_HEAP_TYPE_IOMMU / MULTIMEDIA多媒体专用、支持IOMMU映射、大尺寸连续物理内存QCarCam图像Buffer必选

关键Flag标记(分配时控制缓存特性)

ION_FLAG_CACHED// CPU带缓存,普通业务ION_FLAG_NONCACHED// 无缓存,图像采集首选,避免缓存同步错乱ION_FLAG_WRITECOMBINE// 写合并缓存,GPU显示专用ION_FLAG_SECURE// 安全内存,DMS人脸识别加密帧使用

ION用户态核心API(ion.h)

Android座舱开发自定义QCarCam工具程序,必须引入ion.h,依赖系统库libion.so,核心操作分5步:打开设备→分配内存→地址映射→缓存同步→释放内存。

1. 基础句柄操作

// 打开ION设备节点 /dev/ionintion_fd=open("/dev/ion",O_RDWR);// 关闭设备,进程退出必须执行close(ion_fd);

2. 分配ION物理内存(核心)

// 分配参数结构体struction_allocation_dataalloc={.len=buffer_size,// 单帧NV12总字节.heap_mask=1<<ION_HEAP_TYPE_MULTIMEDIA,// 多媒体堆.flags=ION_FLAG_NONCACHED,// 无缓存,相机必选.align=4096,// 4K页对齐,DMA要求};// ioctl下发分配命令ioctl(ion_fd,ION_IOC_ALLOC,&alloc);// alloc.handle 得到ION内部句柄// alloc.fd 得到dma-buf fd(跨进程共享核心)

3. 地址映射(虚拟地址读取像素)

// 通过ION导出的fd映射用户态虚拟地址void*vaddr=mmap(NULL,buffer_size,PROT_READ|PROT_WRITE,MAP_SHARED,alloc.fd,0);// vaddr 对应 qcarcam_plane_t->virt_addr

4. 缓存同步(解决图像花屏、数据错乱)

DMA硬件直接修改物理内存后,CPU缓存存在旧数据,必须同步:

// 同步结构体struction_sync_datasync={.handle=alloc.handle,.cache_op=ION_CACHE_INVALIDATE// DMA写完,失效CPU缓存};ioctl(ion_fd,ION_IOC_SYNC,&sync);// CPU修改完内存,刷缓存到物理内存,供DMA读取sync.cache_op=ION_CACHE_FLUSH;ioctl(ion_fd,ION_IOC_SYNC,&sync);

QNX PMEM对应API:pmem_invalidate()/pmem_flush(),逻辑完全对齐。

5. 释放内存、关闭句柄

// 释放ION bufferstruction_handle_datafree_data={.handle=alloc.handle};ioctl(ion_fd,ION_IOC_FREE,&free_data);// 解除mmap映射munmap(vaddr,buffer_size);// 关闭dma-buf fdclose(alloc.fd);

6. 跨进程图像共享(dma-buf fd)

ION分配时输出的alloc.fd是全系统唯一内存标识,通过Socket/ binder传递fd给AVM、算法进程,其他进程通过fd重新mmap,零拷贝共享同一块图像Buffer,QCarCam多进程采集依赖该机制。

ION vs PMEM 完整对比(Android座舱 VS QNX智驾)

对比维度Android座舱 IONQNX智驾 PMEM
操作系统Android Linux内核原生标准高通QNX私有定制组件,无Linux原生等价物
头文件ion.hpmem.h
系统库libion.solibpmem.so
内存堆多Heap分层(MULTIMEDIA/IOMMU)统一物理内存池,无Heap区分
跨进程共享dma-buf fd标准,Linux全生态兼容pmem_export_fd/import_fd,QNX私有接口
缓存同步ion_ioctl ION_CACHE_INVALIDATE/FLUSHpmem_invalidate() / pmem_flush()
DMA兼容性标准Linux dma-buf框架,适配所有Linux外设QNX专属DMA,仅适配高通AIS/CSI硬件
QCarCam适配Android IVI版本libqcarcam内置ION封装QNX ADAS版本libqcarcam内置PMEM封装
可移植性所有Android平台通用(MTK/高通)仅高通QNX开发板可用,无跨平台移植性
碎片化优化支持CMA连续内存分配,内存回收完善静态预分配内存池,长期运行易内存耗尽

总结

  1. DMA是硬件搬运通道,负责摄像头、ISP、GPU等外设与内存之间高速传输大块图像数据,解放CPU算力;硬性要求内存必须是连续物理地址。
  2. PMEM是QNX专属内存分配工具,唯一作用是给DMA硬件提供合规的连续物理内存,配套pmem.h提供分配、地址转换、缓存同步、跨进程共享全套API,是QCarCam相机采集的底层依赖。
  3. 二者是配套依存关系:DMA负责“搬数据”,PMEM负责“提供DMA能用的内存”,缺少任意一个都无法实现多路摄像头稳定图像采集;开发时缓存同步、地址区分、内存分配数量是最容易出错的关键点。
  4. 平台差异不可忽略:Android使用ION替代PMEM,两套API、头文件、编译依赖完全隔离,移植代码需要区分系统做分支适配。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询