4. GEM 内存管理:GEM 基础对象、Buffer Object 管理、Dumb Buffer 与 Prime 共享

GEM(Graphics Execution Manager)是 DRM 框架中负责图形缓冲区管理的核心基础设施。它提供了一套统一的内存分配、共享与生命周期管理机制,使得用户空间应用(如 Mesa、Wayland Compositor)与内核驱动之间能够高效地传递显存对象。本章将深入剖析 GEM 的基础对象模型、Buffer Object 的生命周期、Dumb Buffer 的简易分配路径以及跨设备/跨进程的 Prime 共享机制。

4.1 GEM 基础对象模型

GEM 的核心抽象是 GEM Object,它在内核中对应一个 struct drm_gem_object 实例。每个 GEM Object 代表一块由 DRM 驱动管理的物理或虚拟内存区域,并关联一个全局唯一的句柄(Handle)供用户空间引用。

关键概念: GEM Object 本身不直接包含物理页,而是通过驱动提供的 gem_create_object 回调分配,并关联到具体的 dma_buf 或后备存储(Backing Store)。用户空间通过 drmIoctl(DRM_IOCTL_GEM_OPEN) 获取句柄,内核维护引用计数以管理生命周期。
GEM 对象模型结构 用户空间 (FD/Handle) drm_gem_object 驱动后端 (SHMEM/VRAM) 核心属性 • size: 缓冲区大小(字节) • handle: 用户空间句柄(u32) • refcount: 内核引用计数 • dma_buf: 关联的 DMA-BUF 导出对象 • filp: 关联的 struct file(用于 mmap)

4.2 Buffer Object 生命周期管理

Buffer Object(BO)是 GEM Object 在驱动层面的具体化,通常包含物理内存页的分配与绑定。其生命周期遵循“创建 → 绑定 → 使用 → 解绑 → 销毁”的严格流程。

阶段 关键操作 内核函数/IOCTL 说明
创建 分配 GEM Object 与后备内存 drm_gem_create_object() 驱动可重写 gem_create_object 回调
绑定 将物理页映射到 GPU 地址空间 drm_gem_handle_create() 生成用户空间句柄
使用 CPU/GPU 访问缓冲区 mmap / drm_gem_prime_export() 通过 VMA 或 DMA-BUF 共享
解绑 释放句柄与映射关系 drm_gem_handle_delete() 用户空间调用 close() 触发
销毁 释放物理内存与 GEM Object drm_gem_object_free() 引用计数归零时自动触发
避坑指南: 用户空间通过 DRM_IOCTL_GEM_CLOSE 关闭句柄时,内核仅减少引用计数。只有当所有进程的句柄以及内核内部引用(如 GPU 提交)都释放后,GEM Object 才会被真正销毁。务必注意 内存泄漏 与 UAF(Use-After-Free) 问题。

4.3 Dumb Buffer:简化分配路径

Dumb Buffer 是 GEM 提供的一种简化内存分配机制,专为不需要 GPU 复杂内存管理的显示应用(如 FBDEV、简单 KMS 测试)设计。它通过 DRM_IOCTL_MODE_CREATE_DUMB 分配连续或高效的内存块,并自动关联 GEM Object。

Dumb Buffer 分配流程 用户空间 IOCTL drm_mode_create_dumb 驱动回调 返回 Handle/Size Dumb Buffer 特点 • 无需 GPU 驱动支持,仅需 KMS 驱动实现 dumb_create 回调 • 返回的缓冲区可直接用于 mmap,适合 CPU 渲染 • 不支持 GPU 加速操作(如 3D 渲染、硬件合成)
// 用户空间创建 Dumb Buffer 示例
struct drm_mode_create_dumb create = {
    .width = 1920,
    .height = 1080,
    .bpp = 32,
};
int ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
if (ret) {
    perror("Failed to create dumb buffer");
    return -1;
}
printf("Created dumb buffer: handle=%u, pitch=%u, size=%llu\n",
       create.handle, create.pitch, create.size);

4.4 Prime 共享:跨设备与跨进程

Prime 是 GEM 的扩展机制,基于 Linux 内核的 DMA-BUF 框架实现。它允许将 GEM Object 导出为 dma_buf 文件描述符(FD),从而实现跨 DRM 设备、跨进程甚至跨内核子系统的缓冲区共享。

核心流程: 导出端调用 DRM_IOCTL_PRIME_HANDLE_TO_FD 将 GEM Handle 转换为 FD;导入端调用 DRM_IOCTL_PRIME_FD_TO_HANDLE 将 FD 转换为本地 GEM Handle。内核通过 dma_buf 的 attach() / detach() 回调管理设备间映射。
操作 IOCTL 命令 输入 输出 典型场景
导出 DRM_IOCTL_PRIME_HANDLE_TO_FD GEM Handle 文件描述符 (FD) GPU 渲染结果传递给显示控制器
导入 DRM_IOCTL_PRIME_FD_TO_HANDLE 文件描述符 (FD) 本地 GEM Handle 显示控制器读取 GPU 渲染的帧缓冲
同步 DMA_BUF_IOCTL_SYNC FD + 同步标志 无 CPU 访问前确保缓存一致性
Prime 共享架构 进程 A (GPU 驱动) Handle → FD drm_gem_prime_export() dma_buf (FD 共享) 进程 B (显示驱动) FD → Handle drm_gem_prime_import() Prime 共享关键点 • 导出时内核创建 dma_buf 并关联 GEM Object 的 sg_table • 导入时驱动调用 dma_buf_attach() 建立设备映射 • 支持硬件 I/O 一致性(通过 dma_sync_single_for_device)
注意事项: Prime 共享要求两端设备都支持 DMA-BUF 协议,且驱动必须正确实现 gem_prime_export 与 gem_prime_import 回调。对于不支持硬件缓存的设备,需要显式调用 dma_buf_begin_cpu_access() 和 dma_buf_end_cpu_access() 来保证数据一致性。

4.5 总结与对比

特性 GEM Object Dumb Buffer Prime 共享
分配方式 驱动自定义 内核通用回调 基于现有 GEM Object
GPU 加速 支持 不支持 支持(需驱动配合)
跨进程 需手动传递 Handle 仅单进程 通过 FD 原生支持
跨设备 不支持 不支持 支持
典型用途 3D 渲染、合成 FBDEV、简单显示 GPU→Display 零拷贝

理解 GEM 内存管理的这三个层次,是掌握 DRM 显示驱动开发的基础。Dumb Buffer 提供了快速上手的路径,GEM Object 是灵活性的核心,而 Prime 共享则是实现高性能多设备协作的关键。在实际驱动开发中,需要根据硬件能力和应用场景选择合适的分配与共享策略。