Linux drm内存管理(一) 从伙伴系统到BO:GPU内存为何需要专属管家?

Linux drm内存管理(一) 从伙伴系统到BO:GPU内存为何需要专属管家? 1. 从伙伴系统到BOGPU内存的特殊性第一次接触GPU内存管理时我下意识地认为它和CPU内存管理差不多——不就是申请一块内存然后读写吗但真正深入DRM驱动开发后才发现这个想法太天真了。CPU的伙伴系统就像个规整的仓库管理员而GPU内存管理更像是个要同时协调多个仓库的物流主管。伙伴系统管理CPU内存时所有内存都挂在统一的内存总线上物理属性完全一致。但GPU要同时管理多种内存独立显存VRAM、共享系统内存RAM甚至不同厂商的显存可能采用完全不同的总线协议。这就好比你要同时管理普通仓库、冷链仓库和危险品仓库每种仓库的出入库规则都不一样。更麻烦的是GPU的使用模式。CPU通常以字节为单位随机访问内存而GPU操作的最小单位是Buffer ObjectBO——相当于整箱搬运货物。一个BO可能包含纹理、着色器等完整数据单元这些单元在显存中的物理位置对应用程序应该是透明的。这就引出了GPU内存管理的核心挑战如何让开发者像操作普通内存一样使用BO同时在内核层智能处理不同内存区域的物理差异2. GPU内存管理的三大硬伤2.1 内存种类多如繁星上周调试一个AMD显卡时我数了数它要处理的内存类型GDDR6显存、通过PCIe映射的系统内存、APU共享的UMAM内存...这还没算不同电源状态下的内存行为差异。当GPU进入S3睡眠时显存会断电丢失数据而系统内存却保持供电。这种物理属性的差异使得CPU那套一视同仁的内存管理完全失效。我曾在驱动代码中看到这样的注释纹理数据优先放显存计算中间结果可放系统内存。这揭示了一个关键设计哲学GPU内存管理器必须了解不同内存类型的特性才能做出最优分配决策。就像物流系统要知道哪些货物必须冷藏哪些可以常温运输。2.2 总线带宽的紧箍咒用PCIe总线连接GPU就像用吸管喝珍珠奶茶——珍珠数据经常卡住。实测显示PCIe 3.0 x16的带宽只有约16GB/s而DDR4内存带宽轻松突破50GB/s。这个数量级差异导致直接内存访问(DMA)成为必选项。在写驱动时我经常看到这样的优化将频繁访问的BO锁定在显存中减少PCIe传输。这需要内存管理器精确跟踪BO的使用模式就像快递系统要根据包裹投递频率决定存放位置。TTM框架中的内存域(Memory Domain)概念正是为此而生它能区分显存、系统内存等不同存储区域。2.3 以Buffer Object为中心的宇宙传统内存管理关注的是字节地址而GPU的世界围绕BO展开。一个BO可能包含完整的渲染目标其生命周期与图形API对象绑定。这带来两个特殊需求BO需要整体迁移当显存不足时整个BO可能要被交换到系统内存BO需要多重映射既要被GPU着色器访问又要支持CPU通过mmap读写这种使用模式催生了GEM的核心设计——用句柄(handle)抽象BO。应用程序通过句柄操作BO完全不用关心其物理位置。这就像用快递单号追踪包裹无需知道它当前在哪个中转站。3. TTM与GEM的进化之路3.1 TTM全能但复杂的管家早期接触TTM时我被它的代码复杂度震惊了。一个简单的BO分配可能涉及struct ttm_buffer_object *bo; struct ttm_bo_device *bdev; ttm_bo_init(bdev, bo, size, ttm_bo_type_device, ttm_sys_placement, 0, false, NULL, NULL, ttm_bo_destroy);这还没考虑内存迁移、同步等操作。TTM像是个事无巨细都要管的老管家提供了显存/内存的统一分配接口BO迁移的状态机管理内存回收的LRU算法但它的抽象层次太低导致每个显卡驱动都要重复实现大量样板代码。我在AMD驱动中见过长达2000行的TTM回调函数这显然不利于代码维护。3.2 GEM化繁为简的革新GEM的出现就像给TTM套了层友好外壳。它保留了TTM的核心思想但通过以下简化赢得开发者青睐隐藏TTM的复杂状态机暴露简单的create/mmap/destroy接口将内存策略决策下放给驱动实现引入PRIME机制实现跨进程BO共享以最简单的BO创建为例struct drm_gem_object *obj; obj drm_gem_object_alloc(dev, size);GEM的巧妙之处在于它知道何时该装傻。比如当Intel核显驱动处理BO分配时GEM会明智地将决策权交给i915驱动的内存管理器由后者决定使用 stolen memory预留内存还是普通系统内存。4. 实战中的内存管理策略4.1 混合内存的平衡术在笔记本双显卡环境中我经常要调试显存分配策略。好的策略应该考虑活跃度频繁访问的BO优先放显存大小大块BO可能更适合系统内存用途纹理需要高带宽计算缓冲区可以容忍延迟这就像在有限的快递仓库中要把常发货的商品放在门口大件物品放到郊区仓库。TTM的eviction驱逐机制正是为此设计它会根据LRU算法自动迁移不活跃的BO。4.2 DMA-BUF的桥梁作用当需要跨设备共享BO时比如GPU到视频编解码器DMA-BUF就派上用场了。我曾用以下代码实现导出struct dma_buf *export; export drm_gem_prime_export(dev, obj, 0);这个简单的API背后GEM要处理内存类型兼容性检查可能的格式转换同步点管理这相当于为不同快递公司建立统一的包裹交换标准避免每家都用自己的包装规范。5. 为什么不能简单套用伙伴系统最后回到最初的问题伙伴系统那么好用为什么GPU不直接拿来用通过三个实际场景就能明白异构内存管理当显存不足时伙伴系统无法智能地将部分BO迁移到系统内存。就像普通仓库管理员不会主动把货物转移到冷链仓库。设备特性抽象AMD显卡的HBM显存和NVIDIA的GDDR显存访问模式完全不同伙伴系统的页分配器无法适应这种差异。图形API集成Vulkan等现代API要求精细控制内存类型伙伴系统的一视同仁分配策略无法满足需求。在调试一个内存泄漏问题时我亲眼看到错误使用伙伴系统分配显存导致的灾难——GPU连续OOM内存不足崩溃。改用GEM框架后内存管理器能自动平衡显存和系统内存使用问题迎刃而解。