1. 背景与意义
1.1 异构系统与缓冲区共享的挑战
在现代 SoC、嵌入式、图形和多媒体系统中,CPU、GPU、VPU、ISP、DMA 控制器等多个硬件单元需要高效地共享和传递大块数据(如图像帧、视频流、AI 张量等)。如果每个设备都维护独立的缓冲区,数据在设备间传递时就需要频繁拷贝,极大浪费带宽和内存,降低系统性能。
1.2 dma-buf 的目标
dma-buf 框架是 Linux 内核为解决“跨设备零拷贝缓冲区共享”而设计的通用机制。其核心目标包括:
-
跨驱动、跨设备共享物理缓冲区,避免冗余拷贝。
-
标准化缓冲区导出、导入、映射、同步等操作,简化驱动开发。
-
支持多种内存类型(系统内存、显存、专用区域等),适应复杂硬件架构。
-
支持同步机制(fence),保证多设备并发访问的数据一致性。
2. dma-buf 的核心原理
2.1 角色划分
-
Exporter(导出者):负责分配和管理物理缓冲区的驱动(如 GPU、VPU、分配器等)。
-
Importer(导入者):需要访问该缓冲区的其他驱动(如显示控制器、ISP、DMA 控制器等)。
-
dma-buf 核心框架:为 exporter 和 importer 提供标准化的缓冲区共享、映射、同步等接口。
2.2 共享机制
-
Exporter 通过
dma_buf_export()
导出一个物理缓冲区,获得一个struct dma_buf
对象,并为其分配一个匿名文件描述符(fd)。 -
Importer 通过 fd 调用
dma_buf_get()
或dma_buf_attach()
,获得对该缓冲区的访问权。 -
Importer 可通过
dma_buf_map_attachment()
将缓冲区映射到自己的设备地址空间,实现零拷贝访问。 -
多个 importer 可同时 attach 同一个 dma-buf,实现多设备并发访问。
dma-buf机制建立在 anon_inode之上,是 anon_inode技术一个重要应用示例。
2.3 同步机制
-
dma-buf 支持 fence(同步栅栏)机制,保证多设备并发访问时的数据一致性。
-
Exporter/Importer 可通过
dma_resv
对象管理读写 fence,协调访问时序。
3. 关键数据结构与实现
3.1 struct dma_buf_ops
dma_buf_ops
定义了 dma-buf 的所有操作接口,由 exporter 实现。
struct dma_buf_ops {int (*attach)(struct dma_buf *, struct dma_buf_attachment *);void (*detach)(struct dma_buf *, struct dma_buf_attachment *);int (*pin)(struct dma_buf_attachment *attach);void (*unpin)(struct dma_buf_attachment *attach);struct sg_table *(*map_dma_buf)(struct dma_buf_attachment *, enum dma_data_direction);void (*unmap_dma_buf)(struct dma_buf_attachment *, struct sg_table *, enum dma_data_direction);void (*release)(struct dma_buf *);int (*begin_cpu_access)(struct dma_buf *, enum dma_data_direction);int (*end_cpu_access)(struct dma_buf *, enum dma_data_direction);int (*mmap)(struct dma_buf *, struct vm_area_struct *vma);int (*vmap)(struct dma_buf *dmabuf, struct iosys_map *map);void (*vunmap)(struct dma_buf *dmabuf, struct iosys_map *map);
};
-
attach/detach:设备 attach/detach 缓冲区时的回调。
-
pin/unpin:锁定/解锁缓冲区,防止被移动。
-
map/unmap_dma_buf:将缓冲区映射到设备地址空间。
-
release:最后一个引用释放时的清理回调。
-
begin/end_cpu_access:CPU 访问前后的同步操作(如 cache flush)。
-
mmap/vmap/vunmap:内核/用户空间映射支持。
3.2 struct dma_buf
dma_buf
是 dma-buf 的核心对象,描述一个可共享的物理缓冲区。
struct dma_buf {size_t size;struct file *file;struct list_head attachments;const struct dma_buf_ops *ops;unsigned vmapping_counter;struct iosys_map vmap_ptr;const char *exp_name;const char *name;spinlock_t name_lock;struct module *owner;struct list_head list_node;void *priv;struct dma_resv *resv;wait_queue_head_t poll;struct dma_buf_poll_cb_t cb_in, cb_out;// ... 其他成员
};
-
size:缓冲区大小。
-
file:匿名文件,用于 fd 传递和引用计数。
-
attachments:所有 attach 到该缓冲区的设备链表。
-
ops:操作函数集。
-
resv:同步对象,管理 fence。
-
poll/cb_in/cb_out:支持用户空间 poll/epoll 事件通知。
3.3 struct dma_buf_attachment
描述一个设备与 dma-buf 的关联关系。
struct dma_buf_attachment {struct dma_buf *dmabuf;struct device *dev;struct list_head node;bool peer2peer;const struct dma_buf_attach_ops *importer_ops;void *importer_priv;void *priv;
};
-
dmabuf:关联的 dma-buf。
-
dev:关联的设备。
-
priv:exporter/importer 的私有数据。
3.4 struct dma_buf_export_info
导出 dma-buf 时的参数描述。
struct dma_buf_export_info {const char *exp_name;struct module *owner;const struct dma_buf_ops *ops;size_t size;int flags;struct dma_resv *resv;void *priv;
};
4. 主要 API 及用法
4.1 Exporter 侧
4.1.1 导出缓冲区
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
exp_info.ops = &my_dma_buf_ops;
exp_info.size = buffer_size;
exp_info.priv = my_priv_data;
struct dma_buf *dmabuf = dma_buf_export(&exp_info);
4.1.2 获取 fd
int fd = dma_buf_fd(dmabuf, O_CLOEXEC);
4.1.3 释放缓冲区
dma_buf_put(dmabuf);
4.2 Importer 侧
4.2.1 通过 fd 获取 dma-buf
struct dma_buf *dmabuf = dma_buf_get(fd);
4.2.2 设备 attach
struct dma_buf_attachment *attach = dma_buf_attach(dmabuf, dev);
4.2.3 映射到设备地址空间
struct sg_table *sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
4.2.4 解除映射与 detach
dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
dma_buf_detach(dmabuf, attach);
4.2.5 释放 dma-buf
dma_buf_put(dmabuf);
4.3 CPU 访问同步
dma_buf_begin_cpu_access(dmabuf, DMA_FROM_DEVICE);
// CPU 访问缓冲区
dma_buf_end_cpu_access(dmabuf, DMA_FROM_DEVICE);
4.4 用户空间 mmap
mmap(fd, ...); // 通过 fd 直接 mmap 到用户空间
5. dma-buf 的同步机制
5.1 dma_resv 与 fence
-
dma-buf 通过
struct dma_resv
管理同步 fence,协调多设备并发访问。 -
Exporter/Importer 在读写缓冲区前后添加/等待 fence,保证数据一致性。
-
支持隐式同步(如 GPU/显示管线)和显式同步(如用户空间 fence)。
5.2 begin_cpu_access/end_cpu_access
-
在 CPU 访问前后,需调用
begin_cpu_access
/end_cpu_access
,由 exporter 实现 cache flush、内存屏障等操作,保证数据一致性。
6. dma-buf 的典型应用场景
6.1 GPU-显示管线零拷贝
-
GPU 渲染输出帧缓冲区,通过 dma-buf fd 传递给显示控制器(DRM/KMS),实现零拷贝显示。
6.2 摄像头-ISP-GPU 协作
-
摄像头驱动分配帧缓冲区,ISP 处理后通过 dma-buf fd 传递给 GPU 进行后处理或 AI 推理。
6.3 多媒体编解码
-
VPU 解码器输出缓冲区,通过 dma-buf fd 传递给 GPU/显示/AI 单元,实现高效视频播放和处理。
6.4 用户空间跨进程共享
-
用户空间应用通过 dma-buf fd 在不同进程间共享大块数据(如图像、视频帧、AI 张量等)。
7. dma-buf 的内核实现细节
7.1 文件描述符与引用计数
-
每个 dma-buf 对象都对应一个匿名文件(anon_inode),通过 fd 传递和引用计数。anon_inode技术参见博文:Linux 内核 anon_inode 机制详解与应用。
-
内核通过
get_dma_buf
/dma_buf_put
管理生命周期。
7.2 设备 attach/detach
-
每个 importer attach 时,exporter 可检查设备兼容性、DMA 约束等,拒绝不支持的设备。
-
detach 时清理相关资源。
7.3 映射与同步
-
map/unmap_dma_buf 负责将缓冲区映射到 importer 设备地址空间,支持多种内存类型和 DMA 方向。
-
begin/end_cpu_access 负责 cache flush、内存屏障等同步操作。
7.4 poll/epoll 支持
-
dma-buf 支持 poll/epoll 机制,用户空间可通过 poll/epoll 监控缓冲区 fence 状态,实现异步事件通知。
8. 总结
-
dma-buf 是 Linux 内核支持异构系统、跨设备零拷贝缓冲区共享的核心机制。
-
通过标准化的导出、导入、映射、同步等接口,极大简化了驱动开发和系统集成。
-
支持多种内存类型、同步机制和异步事件通知,适应复杂硬件和高性能场景。
-
正确实现和使用 dma-buf 能显著提升系统性能、带宽利用率和开发效率。