完整教程:第三章:GEM分析:3.1 GEM设计目标和核心概念
1. 背景与需求
1.1 GPU内存管理的挑战
现代图形应用以及AI应用程序需要大量的显存来存储帧缓冲区(framebuffers)、纹理(textures)、顶点缓冲(vertices)等图形相关数据和各种AI model以及训练数据。由于这些数据的高度动态特性,高效地管理显存存对图形栈和计算栈至关重要,并在DRM基础设施中扮演核心角色。
具体挑战包括:
- 大容量需求:GPU需要存储大量的图形数据和训练数据
- CPU-GPU数据共享:CPU和GPU之间需要频繁共享和传输数据
- 多进程安全共享:多个用户空间进程需要安全地共享GPU资源,同时保证进程隔离
1.2 传统方案的问题
在GEM出现之前,DRM的内存管理存在以下问题:
- 功能有限:早期DRM只提供framebuffer管理,缺乏通用的GPU内存对象抽象
- 驱动碎片化:各驱动各自实现内存管理,缺乏统一接口,代码重复
- 文件描述符限制:最初考虑使用文件描述符(fd)作为对象句柄,但存在致命缺陷:
- 进程默认限制只能打开1024个左右的文件描述符
- 高fd值会影响X Server的select()处理性能,也影响GL客户端应用
- 生命周期管理困难:用户空间难以高效管理GPU对象的生命周期
- AI训练推理的新需求带来的挑战
正如drivers/gpu/drm/drm_gem.c注释所述:
“The goal was to have swap-backed object allocation managed through struct file. However, file descriptors as handles to a struct file have two major failings: Process limits prevent more than 1024 or so being used at a time by default. Inability to allocate high fds will aggravate the X Server’s select() handling.”
2. 设计目标
基于上述挑战,GEM的设计目标明确定义为:
2.1 统一的内存对象抽象
- 提供通用的GPU内存对象(GEM Object)表示
- 屏蔽底层硬件差异,但允许驱动扩展
- 数据无关性:GEM管理抽象的缓冲对象,不需要知道单个缓冲区的具体内容
2.2 简化的内存管理
GEM的设计哲学与TTM完全不同。如Documentation/gpu/drm-mm.rst所述:
“GEM started as an Intel-sponsored project in reaction to TTM’s complexity. Its design philosophy is completely different: instead of providing a solution to every graphics memory-related problems, GEM identified common code between drivers and created a support library to share it.”
关键特性:
- 简化的初始化和执行需求:相比TTM更轻量级
- 支持标准操作:内存分配/释放、映射、命令执行等
2.3 安全的多进程访问
- 基于handle的访问控制:避免直接暴露内核指针
- 进程隔离:每个进程维护独立的handle命名空间
- 引用计数管理:自动管理对象生命周期
2.4 灵活的扩展性
- 核心只提供共享的公共代码
- 设备特定操作(如命令执行、pinning、domain管理)留给驱动通过私有ioctl实现
- 驱动可以扩展
drm_gem_object结构,添加私有数据
3. 核心概念与设计
3.1 drm_gem_object:核心对象抽象
drm_gem_object是GEM的核心数据结构,定义在include/drm/drm_gem.h:
关键特性:
- 引用计数管理:通过
kref refcount实现自动生命周期管理- 使用
drm_gem_object_get()获取引用 - 使用
drm_gem_object_put()释放引用
- 使用
- 双重计数机制:
handle_count独立追踪用户空间handle数量 - 灵活的存储:
- 可使用shmem作为后端(
filp字段) - 也支持驱动私有存储(private GEM objects,
filp为NULL)
- 可使用shmem作为后端(
- 可扩展性:驱动通过嵌入
drm_gem_object创建自己的对象类型
3.2 Handle机制:用户空间接口
GEM采用32位整数handle作为用户空间对对象的引用,而不是文件描述符。这一设计解决了fd的限制问题。
实现机制(源自drivers/gpu/drm/drm_gem.c):
/**
* This led to a plan of using our own integer IDs (called handles, following
* DRM terminology) to mimic fds, and implement the fd syscalls we need as
* ioctls.
*/
Handle管理:
- 每个进程(
drm_file)维护独立的handle到对象的映射表(通过IDR实现) - Handle创建:
drm_gem_handle_create()分配handle并建立映射 - Handle查找:
drm_gem_object_lookup()根据handle查找对象 - Handle删除:
drm_gem_handle_delete()移除映射并释放引用
安全性保证:
- 进程A的handle 5和进程B的handle 5可以指向不同的对象
- 内核通过
drm_file上下文隔离不同进程的handle空间 - 对象访问权限通过
drm_vma_node_allow()/revoke()控制
3.3 drm_vma_offset_manager:地址空间管理
为了支持mmap操作,GEM需要为每个对象分配一个虚拟地址空间的偏移量。drm_vma_offset_manager负责管理这个伪造的地址空间。
设计目标(源自drivers/gpu/drm/drm_vma_manager.c):
/**
* The vma-manager is responsible to map arbitrary driver-dependent memory
* regions into the linear user address-space. It provides offsets to the
* caller which can then be used on the address_space of the drm-device.
*/
3.4 drm_mm:通用范围分配器
drm_mm是GEM的底层内存分配器,管理连续的地址空间范围。
设计特点(源自drivers/gpu/drm/drm_mm.c):
/**
* drm_mm provides a simple range allocator. The drivers are free to use the
* resource allocator from the linux core if it suits them, the upside of drm_mm
* is that it's in the DRM core.
*/
四、核心概念关系图
用户空间应用
│
├─ handle (32位整数)
│ │
│ ├─ DRM_IOCTL_GEM_CLOSE
│ ├─ DRM_IOCTL_GEM_FLINK (全局名称)
│ └─ 驱动私有ioctl
│
↓
┌──────────────────────────────────────┐
│ drm_file (进程上下文) │
│ - object_idr: handle→object映射 │
│ - prime: dma-buf handle映射 │
└──────────────────────────────────────┘
│
↓
┌──────────────────────────────────────┐
│ drm_gem_object (核心抽象) │
│ - refcount: 引用计数 │
│ - handle_count: handle计数 │
│ - vma_node: 地址空间节点 │
│ - filp: shmem文件(可选) │
│ - funcs: 驱动操作函数 │
└──────────────────────────────────────┘
│
├─────────────────┬─────────────────┐
│ │ │
↓ ↓ ↓
┌─────────────┐ ┌──────────────┐ ┌──────────────┐
│drm_vma_ │ │ shmem/ │ │ 驱动私有 │
│offset_ │ │ 私有存储 │ │ 扩展字段 │
│manager │ │ │ │ │
│(mmap偏移) │ │ │ │ (如amdgpu_ │
└─────────────┘ └──────────────┘ │ bo) │
│ └──────────────┘
├─ drm_mm (底层范围分配器)
│ - interval_tree: 区间树
│ - holes_size/addr: hole管理
│ - 支持对齐、颜色、多种策略
│
↓
物理/虚拟地址空间
5. GEM的优势
5.1 简洁性
- 相比TTM的"一刀切"方案,GEM更轻量级
- 初始化简单,不需要复杂的配置
- 适合简单的UMA设备
5.2 灵活性
- 只提供共享的公共功能,不强加不必要的复杂性
- 驱动可以自由扩展和定制
- 通过函数指针表(
drm_gem_object_funcs)实现多态
5.3 标准化
- 统一的用户态API(虽然部分操作仍是驱动特定的)
- 标准的handle管理和对象生命周期
- 促进了代码共享和复用
6. GEM的局限性
6.1 功能范围有限
- 不处理视频RAM管理:GEM没有VRAM分配和管理能力
- 不支持内存迁移:无法在系统内存和VRAM间迁移对象
- 不管理placement策略:无法指定内存域(memory domains)优先级
6.2 主要面向UMA设备
如Documentation/gpu/drm-mm.rst所述:
“GEM has simpler initialization and execution requirements than TTM, but has no video RAM management capabilities and is thus limited to UMA devices.”
6.3 缺乏复杂的资源管理
- 没有内存预算管理
- 没有内存驱逐(eviction)策略
- 没有自动的内存迁移和放置优化
这些局限性正是引入TTM的原因。现代DRM驱动通常:
- 使用GEM作为基础对象抽象和handle管理
- 在需要复杂内存管理时,在GEM之上使用TTM
- AMD等驱动通过
amdgpu_bo扩展GEM对象,整合TTM功能
7. 总结
GEM作为DRM的图形执行管理器,通过以下核心设计实现了其目标:
- drm_gem_object:提供统一的GPU内存对象抽象
- Handle机制:解决文件描述符限制,实现安全的多进程隔离
- drm_vma_offset_manager:管理mmap虚拟地址空间
- drm_mm:提供灵活高效的底层范围分配器
GEM的设计哲学是"提供共享的公共代码,而不是试图解决所有问题"。这使得它简洁、灵活且易于扩展。对于需要更复杂内存管理的场景(如独立显卡的VRAM管理),驱动可以在GEM基础上整合TTM等更高级的内存管理器。
GEM与TTM的关系是互补而非替代:GEM提供基础抽象和用户接口,TTM提供高级内存管理功能。现代GPU驱动通常同时使用两者,充分发挥各自的优势。

浙公网安备 33010602011771号