核心认知:三个角色,两种地址
CPU (你的代码) → 只能用虚拟地址 → 需要 mmap 建立映射
DMA 硬件 → 只能用物理地址 → 不需要 mmap,直接访问
内核驱动 → 管理物理地址 → 创建 dma-buf 让硬件共享内存
硬件永远不需要 mmap。mmap 是给 CPU 用的。
dma-buf:硬件之间的"通行证"
dma_buf fd = 42 (你拿到的整数)
│
▼
┌───────────────────────┐
│ 物理地址: 0x80000000 │ ← DMA 引擎直接用
│ 大小: 3MB │
└───────────────────────┘
用途:把一块物理内存的"访问权"在不同硬件模块(V4L2/RGA/MPP/DRM)之间传递,全程硬件直接读写,CPU 不碰数据。
V4L2 两种内存模式
模式一:MMAP(内存由 V4L2 分配)
初始化:
REQBUFS → 内核分配4块物理内存
QUERYBUF × 4 → 查询每块信息
mmap × 4 → ← 必须做!API 强制要求(即使你不用 CPU 读)
QBUF × 4 → 空buf入队
STREAMON
取帧:
DQBUF → 拿到一帧
EXPBUF → 导出 dma-buf fd ← 这时才创建 dma_buf,拿到 fd
把 fd 给 DRM/RGA 做零拷贝
为什么必须 mmap? V4L2 API 历史设计(1999年,早于 dma-buf 的 2012年),驱动状态机要求 STREAMON 前每个 buf 必须已 mmap。
EXPBUF 做了什么? 不是分配新内存,是为已有的物理内存"补办"一个 dma-buf 对象并返回 fd。好比酒店前台补办一张访客卡。
模式二:DMABUF(内存由你分配)
初始化:
DMA_HEAP_ALLOC → 你自己分配 dma-buf fd
REQBUFS(memory=DMABUF) → 告诉V4L2用外部内存
QBUF(你的fd) → 把你的 fd 入队
STREAMON
取帧:
DQBUF → 拿到一帧
直接用你自己的 fd 给 DRM/RGA ← 不需要 EXPBUF!fd 本来就是你的
全程不需要 mmap,不需要 EXPBUF。 因为内存是你分配的,fd 生来就是你的。
为什么 GStreamer 解码器不需要 mmap?
GStreamer 解码器:
MPP 驱动内部分配内存 → 创建 dma_buf → 返回 fd 给 GstBuffer
你拿到的是"成品 fd",直接能用
V4L2 MMAP 模式:
V4L2 驱动分配内存 → 你必须 mmap 初始化 → 然后 EXPBUF 借出 fd
你是"内存的管理者",必须走完流程
区别:一个是接收者(GStreamer),一个是管理者(V4L2 MMAP)。
总结
| MMAP 模式 | DMABUF 模式 | GStreamer 解码 | |
|---|---|---|---|
| 内存主人 | V4L2 驱动 | 你 | MPP 驱动 |
| 需要 mmap | ✅ 强制 | ❌ | ❌ |
| 需要 EXPBUF | ✅ | ❌ | ❌ |
| fd 来源 | EXPBUF 导出 | 你自己创建的 | 驱动直接给 |
一句话: dma-buf 是硬件之间的零拷贝通道,mmap 是给 CPU 看的翻译通道。V4L2 MMAP 模式强制 mmap 是 API 历史包袱,不是技术必需。用 DMABUF 模式可跳过。