4. Buffer Sharing 机制:DMA-BUF 如何实现跨驱动、跨设备的零拷贝内存共享
在多媒体与图形渲染管线中,数据需要在 GPU、Display Controller、Camera ISP、Video Codec 等多个硬件单元之间流转。传统的拷贝方式不仅消耗 CPU 带宽,还会引入延迟和功耗开销。DMA-BUF(Direct Memory Access Buffer)是 Linux 内核提供的一种通用缓冲区共享机制,它允许不同设备驱动程序通过 文件描述符(fd) 传递物理内存的访问权,从而实现真正的零拷贝共享。
核心目标: 避免数据在设备间逐级拷贝,通过共享同一块物理内存,让多个硬件单元直接访问,降低延迟与功耗。
4.1 DMA-BUF 的核心架构与对象模型
DMA-BUF 在内核中抽象为 struct dma_buf 对象,它并不直接管理物理页面,而是通过 附件(Attachment) 和 映射(SGT / Scatter-Gather Table) 机制,将物理内存暴露给不同的驱动。
4.2 零拷贝共享的完整生命周期
DMA-BUF 的共享流程分为 导出(Export)、附加(Attach)、映射(Map) 和 同步(Sync) 四个阶段。以下以 Camera 采集帧送给 GPU 渲染为例:
| 阶段 | 调用接口 | 动作描述 | 参与者 |
|---|---|---|---|
| 1. 导出 | dma_buf_export() |
Camera 驱动分配物理页面,创建 dma_buf 对象,返回 fd | Camera ISP 驱动 |
| 2. 传递 | fd 通过 IPC / ioctl 传递 | 用户空间将 fd 从 Camera 节点传递给 GPU 节点 | 用户空间 / libcamera |
| 3. 附加 | dma_buf_attach() |
GPU 驱动创建 attachment,记录设备与内存访问权限 | GPU 驱动 |
| 4. 映射 | dma_buf_map_attachment() |
生成 SGT,将物理页面映射到 GPU 的 IOMMU 地址空间 | GPU 驱动 / IOMMU |
| 5. 同步 | dma_buf_begin/end_cpu_access() |
保证 Cache 一致性,防止 CPU 与设备访问冲突 | CPU / 设备驱动 |
性能关键: 第 4 步的 SGT 映射是零拷贝的核心。GPU 通过 IOMMU 直接访问 Camera 的物理页面,无需任何数据搬移。映射完成后,GPU 可以直接将帧数据作为纹理采样。
4.3 跨设备共享的 DMA 地址转换
不同设备可能挂载在不同的 IOMMU 域或总线(如 PCIe、AMBA)上,物理页面需要转换为设备可见的 DMA 地址。DMA-BUF 通过 Scatter-Gather Table (SGT) 为每个附件独立维护地址映射:
4.4 同步与 Cache 一致性管理
当 CPU 或设备需要访问共享缓冲区时,必须通过 DMA-BUF 同步接口 保证数据一致性。Linux 内核提供了两种同步方向:
- DMA_TO_DEVICE:CPU 写入完成,设备即将读取(Cache 刷出)。
- DMA_FROM_DEVICE:设备写入完成,CPU 即将读取(Cache 失效)。
- DMA_BIDIRECTIONAL:双向访问,需要完整的 Cache 维护。
常见陷阱: 如果驱动在映射后未调用
dma_buf_end_cpu_access() 就启动 DMA 传输,可能导致设备读到过时的 Cache 数据,造成画面撕裂或计算错误。在 Vblank 中断上下文中尤其要注意同步顺序。
4.5 典型应用场景:GPU 与 Display 的帧共享
在 Android 或 Linux 桌面合成器中,DMA-BUF 是连接 GPU 渲染与 Display 扫描输出的关键桥梁:
- GPU 渲染完成后,将 framebuffer 导出为 DMA-BUF fd。
- Display Controller 驱动通过
dma_buf_attach()获取该 fd。 - 在 Vblank 中断到来时,Display 驱动切换 scanout 地址到该 DMA-BUF 的物理页面。
- 整个过程中,framebuffer 数据从未离开物理内存,实现零拷贝合成。
4.6 性能对比:传统拷贝 vs DMA-BUF 共享
| 指标 | 传统拷贝(CPU memcpy) | DMA-BUF 零拷贝共享 |
|---|---|---|
| 延迟 | 高(μs 级拷贝 + 上下文切换) | 极低(仅指针传递 + IOMMU 映射) |
| CPU 占用 | 高(每次拷贝消耗 CPU 周期) | 几乎为零(仅管理开销) |
| 内存带宽 | 额外消耗(读 + 写) | 无额外消耗 |
| 功耗 | 高(频繁内存访问) | 低(数据不动,只动指针) |
| 适用场景 | 小数据量、跨 CPU 核 | 大帧数据、跨设备流水线 |
实战建议: 在 Vblank 中断驱动的合成管线中,优先使用 DMA-BUF 传递 framebuffer。配合
dma_fence 机制,可以精确控制 GPU 渲染完成与 Display 扫描开始的时序,避免撕裂。