为什么需要 DMA-BUF/PRIME传统显存缓冲区如 DRM framebuffer被单个驱动 / 进程独占跨进程 / 跨 GPU 传输数据时需要CPU 拷贝从显存→内存→另一块显存效率极低。DMA-BUF 本质是一个跨驱动共享的内存对象PRIME 则是 DRM 子系统基于 DMA-BUF 实现的缓冲区共享协议二者结合实现了零拷贝的显存共享。关键术语解释DMA-BUFLinux 内核提供的通用缓冲区共享框架核心是一个struct dma_buf对象封装了物理连续 / 非连续的内存区域支持跨驱动的引用计数、映射、同步等操作。PRIMEDRM 定义的一套接口ioctl 命令让 DRM 驱动可以导出export自己的缓冲区为 DMA-BUF或导入import其他驱动导出的 DMA-BUF 作为本地缓冲区使用。Exporter创建 DMA-BUF 并将其导出的驱动 / 进程如显卡 A 驱动。Importer导入并使用 DMA-BUF 的驱动 / 进程如显卡 B 驱动、视频解码驱动。PRIME 基于 DMA-BUF 的共享过程DMA-BUF 共享帧缓冲区帧缓冲区Framebuffer是显存中用于存储一帧图像数据的核心区域DMA-BUF 共享帧缓冲区是 DRM/PRIME 最核心、最常用的场景本质是将帧缓冲区封装为 DMA-BUF 对象实现跨进程 / 跨驱动 / 跨 GPU的零拷贝共享比如硬件解码的帧直接给显示驱动渲染、多 GPU 间帧数据共享。共享帧缓冲区的核心是将帧缓冲区导出为 DMA-BUF 文件描述符fd→ 传递 fd 到目标进程 / 驱动 → 目标端导入 DMA-BUF 并还原为可操作的帧缓冲区。整个流程无 CPU 拷贝数据直接在显存中被多端访问这也是音视频播放、多屏显示、GPU 协同渲染的性能核心。关键要点说明格式匹配导入 / 导出的帧缓冲区必须保证像素格式如DRM_FORMAT_XRGB8888/DRM_FORMAT_NV12、宽高、行间距pitch完全一致否则会出现花屏 / 黑屏。同步机制实际场景中需通过dma_buf_sync或 DRM 同步对象syncobj保证「解码写帧」和「显示读帧」的时序避免显示未解码完成的帧。权限与生命周期DMA-BUF fd 可通过 UNIX 域套接字、fork 继承等方式跨进程传递DMA-BUF 的生命周期由引用计数管理所有导入端关闭 fd 后内核才会释放显存。跨 GPU 共享若解码和显示使用不同 GPU如独显解码 核显显示需确保内核支持「DMA-BUF 跨设备映射」主流内核 5.0 均支持。DMA-BUF 共享帧缓冲区的核心是「fd 传递 零拷贝访问」导出端将帧缓冲区转为 DMA-BUF fd导入端通过 fd 还原为本地可操作的帧缓冲区。实战中需重点保证帧格式一致和同步机制否则会出现显示异常或数据竞争。该机制是高性能音视频播放、多 GPU 渲染的核心相比传统 CPU 拷贝性能提升可达数倍甚至数十倍。用户态使用 PRIME 导出 / 导入 DMA-BUF以下是简化的用户态代码展示如何通过 DRM PRIME 接口导出 / 导入缓冲区需依赖libdrm库。前置条件安装依赖sudo apt install libdrm-devUbuntu/yum install libdrm-develCentOS。确保内核支持 DRM、DMA-BUF主流 Linux 内核均默认支持。1. 导出 DMA-BUFExporter 端#include stdio.h #include fcntl.h #include drm/drm.h #include drm/drm_mode.h int main() { int drm_fd, buf_fd, prime_fd; struct drm_mode_create_dumb create {0}; struct drm_prime_handle prime {0}; // 1. 打开DRM设备以显卡为例路径可能为/dev/dri/card0 drm_fd open(/dev/dri/card0, O_RDWR); if (drm_fd 0) { perror(open drm device failed); return -1; } // 2. 创建DRM dumb缓冲区普通显存缓冲区 create.width 1920; create.height 1080; create.bpp 32; // 每像素32位RGBA if (ioctl(drm_fd, DRM_IOCTL_MODE_CREATE_DUMB, create) 0) { perror(create dumb buffer failed); close(drm_fd); return -1; } // 3. 导出为DMA-BUFPRIME接口 prime.handle create.handle; prime.flags DRM_CLOEXEC; // 子进程执行时关闭fd if (ioctl(drm_fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, prime) 0) { perror(export dma-buf failed); close(drm_fd); return -1; } prime_fd prime.fd; // 这就是DMA-BUF的文件描述符可传递给其他进程 printf(导出的DMA-BUF fd: %d\n, prime_fd); // 4. 后续传递prime_fd给Importer如通过UNIX域套接字、fork后继承 // ... 此处省略fd传递逻辑 ... // 5. 资源释放实际场景需按需释放 close(prime_fd); close(drm_fd); return 0; }2. 导入 DMA-BUFImporter 端#include stdio.h #include fcntl.h #include drm/drm.h #include drm/drm_mode.h int main(int argc, char *argv[]) { if (argc ! 2) { fprintf(stderr, Usage: %s dma_buf_fd\n, argv[0]); return -1; } int drm_fd, dma_buf_fd, handle; struct drm_prime_handle prime {0}; // 1. 打开DRM设备 drm_fd open(/dev/dri/card0, O_RDWR); if (drm_fd 0) { perror(open drm device failed); return -1; } // 2. 接收DMA-BUF fd此处从命令行参数传入实际为跨进程传递 dma_buf_fd atoi(argv[1]); // 3. 导入DMA-BUF为本地DRM handlePRIME接口 prime.fd dma_buf_fd; prime.flags DRM_CLOEXEC; if (ioctl(drm_fd, DRM_IOCTL_PRIME_FD_TO_HANDLE, prime) 0) { perror(import dma-buf failed); close(drm_fd); return -1; } handle prime.handle; // 本地DRM缓冲区句柄可直接操作 printf(导入后的DRM handle: %d\n, handle); // 4. 后续使用handle创建framebuffer、渲染等零拷贝访问 // ... 此处省略渲染逻辑 ... // 5. 资源释放 close(drm_fd); return 0; }实际应用场景多 GPU 渲染如独显渲染 3D 内容核显负责显示通过 DMA-BUF 共享帧缓冲区避免 CPU 拷贝。视频解码 显示硬件解码驱动如 VAAPI将解码后的帧导出为 DMA-BUFDRM 驱动直接导入并显示提升播放性能。跨进程渲染如 Wayland compositor窗口管理器与客户端进程共享缓冲区实现窗口绘制。总结DMA-BUF是内核层通用的共享内存框架封装了可跨驱动共享的显存 / 内存缓冲区PRIME是 DRM 基于 DMA-BUF 的具体实现提供了导出 / 导入缓冲区的标准接口。核心价值是零拷贝共享显存避免 CPU 参与数据传输大幅提升跨驱动 / 跨进程的显存操作效率。关键操作是 “导出fd→ 传递fd→ 导入handle”fd 是 DMA-BUF 在用户态的唯一标识可跨进程传递。