同一个灵魂,两副躯体——GPU 内存管理为何是 CPU MM 的“平行宇宙”

在深入分析drm gpuvm的设计和实现前,我想先给出理解这个领域的一个视角,视角对了,一切设计就是水到渠成。视角如下: Linux 内核 GPU 内存管理子系统(TTM、GEM、GPUVM)与经典 CPU 内存管理(MM)子系统之间的结构和功能具有高度相似性。我们认为,GPU 内存栈不仅仅是受 CPU MM 启发------它是在不同硬件约束下对相同基本原则的重新推导。这种类比并非偶然,而是架构上的必然,它通过共享的术语、相同的算法模式以及趋同的设计轨迹深深嵌入内核的 DRM 子系统中。


1. 论点

Linux GPU 内存管理子系统(TTM/GEM/GPUVM)构成了经典 CPU 虚拟内存概念的领域特定重新实现------包括虚拟地址空间、按需分页、基于 LRU 的驱逐和交换------以适应离散加速器内存层次结构的约束。

这一论点由以下证据支持:

  1. 直接的词汇借用(swap_storageTTM_TT_FLAG_SWAPPEDttm_tt_swapin()
  2. 同构的数据结构设计(drm_gpuvmmm_structdrm_gpuvavm_area_struct
  3. 相同的算法策略(LRU 驱逐扫描、shrinker 集成、故障驱动的填充)
  4. 趋同的演化(GPU 故障处理通过 SVM/HMM 向 CPU 风格的按需分页发展)

2. 论证

2.1 地址空间管理

CPU MM GPU MM(DRM) 结构角色
mm_struct struct drm_gpuvm 每上下文的虚拟地址空间容器
vm_area_struct struct drm_gpuva 映射到后备对象的连续 VA 区域
VMA 红黑树 GPUVM 区间红黑树 用于快速查找的空间索引
mmap() / munmap() drm_gpuvm_sm_map() / drm_gpuvm_sm_unmap() 面向用户的 VA 空间变更
部分解映射时的 VMA 分割/合并 drm_gpuva_op_remap(分割) 维护 VA 空间一致性

drm_gpuvm 文档指出:

"DRM GPU VA 管理器使用 maple_tree 结构来跟踪 GPU 的虚拟地址空间......每个 GPU 虚拟地址空间应该有一个管理器实例。"

这在功能上与 mm_struct 管理进程的虚拟地址空间完全相同。drm_gpuvm 中的 kernel_alloc_node 直接对应于进程 VA 空间中的内核保留地址范围。

2.2 后备存储和放置

CPU MM GPU MM(TTM) 结构角色
物理 RAM(zones) VRAM(TTM_PL_VRAM 快速、有限的主存储
交换设备 系统内存(TTM_PL_SYSTEM 较慢、较大的溢出存储
struct page struct ttm_resource 物理分配跟踪单元
页帧分配(buddy) gpu_buddy_alloc_blocks() 二的幂次方物理分配器
NUMA 节点亲和性 ttm_place.mem_type 放置偏好层次结构

TTM 放置系统(包含有序 struct ttm_place 数组的 struct ttm_placement)类似于 NUMA 内存策略------表达内存应该物理驻留位置的偏好层次。

2.3 交换类比

这是最明确的类比,TTM 直接采用了 CPU MM 的术语:

c 复制代码
struct ttm_tt {
    struct page **pages;
    #define TTM_TT_FLAG_SWAPPED  BIT(0)   // ← CPU 交换术语
    struct file *swap_storage;             // ← shmem 后备,类似交换
    ...
};

TTM 中驱逐到系统内存的路径在结构上与页面换出完全相同:

CPU 换出 GPU 驱逐(TTM)
通过 LRU 扫描选择受害者 通过 ttm_resource_manager.lru[] 选择 BO
将页面内容写入交换设备 将 BO 内容移动到系统内存 / shmem(swap_storage
用交换条目替换 PTE 更新 ttm_resource 放置;设置 TTM_TT_FLAG_SWAPPED
释放物理页帧 释放 VRAM 分配

换入/故障恢复路径:

CPU 换入 GPU 重新验证
页面故障触发 do_swap_page() 命令提交触发 drm_gpuvm_validate()
从交换读取,分配页帧 调用 ttm_bo_validate() → 分配 VRAM,复制回来
安装指向新帧的 PTE 更新 GPU 页表条目
清除交换条目 清除 TTM_TT_FLAG_SWAPPED;调用 ttm_tt_swapin()

GEM VRAM 文档明确指出:

"如果 VRAM 中没有更多空间,不活跃的 GEM 对象可以移动到系统内存。"

这句话是以下 CPU 等价表述的 GPU 版本:"如果没有更多物理 RAM,不活跃的页面可以移动到交换空间。"

2.4 基于 LRU 的驱逐和回收

两个子系统都使用 LRU(最近最少使用)作为主要的驱逐策略:

CPU MM:

  • 每个内存区的活跃/不活跃 LRU 列表
  • kswapd 守护进程执行后台回收
  • slab 缓存的 shrinker 回调
  • lru_gen(多代 LRU)用于改进老化

GPU MM(TTM/GEM):

  • ttm_resource_manager.lru[TTM_MAX_BO_PRIORITY] --- 每种内存类型的优先级感知 LRU
  • 带有 drm_gem_lru_scan()drm_gem_lru --- GEM 对象的 shrinker 集成
  • ttm_pool_type.shrinker_list --- TTM 页面池注册为内核 shrinker
  • drm_mm_scan_init() / drm_mm_scan_add_block() --- 用于连续驱逐的 LRU 扫描

DRM MM 文档描述了驱逐扫描模式:

"使用 drm_mm_scan_add_block() 添加驱逐候选者,直到找到合适的空洞或没有更多可驱逐的对象。"

这在算法上与 CPU MM 的 shrink_inactive_list() 扫描候选者直到释放足够内存完全相同。

2.5 按需分页和故障处理

CPU MM GPU MM 结构角色
handle_mm_fault() GEM 故障处理器(vm_operations_struct.fault 按需页面/BO 填充
延迟分配(首次访问时分配) 首次使用时的 ttm_tt_populate() 推迟物理分配
FAULT_FLAG_WRITE → CoW 写时锁定 / 访问时迁移 访问类型特定处理

GEM 文档指出:

"驱动程序负责通过为每个页面调用 shmem_read_mapping_page_gfp() 来实际分配物理页面。注意,他们可以选择在初始化 GEM 对象时分配页面,或者推迟分配直到需要内存时(例如当发生页面故障时)。"

这是标准的按需分页,移植到了 GPU 领域。

2.6 Madvise 和可清除性

CPU MM GPU MM 结构角色
madvise(MADV_DONTNEED) DRM_GEM_OBJECT_PURGEABLE / shmem.madv 提示:内存可以被回收
madvise(MADV_WILLNEED) 预取操作(DRM_GPUVA_OP_PREFETCH 提示:内存即将被需要
页面标记为干净 → 无需写回即可释放 ttm_backup_flags.purge --- 无需备份即可释放 优化:跳过写回

2.7 休眠作为完全换出

TTM 甚至通过将系统休眠视为完全换出来处理:

c 复制代码
int ttm_device_prepare_hibernation(struct ttm_device *bdev);
// "将 GTT BO 移动到 shmem 以进行休眠"

这是 CPU MM 在挂起到磁盘期间将所有活跃页面写入交换的 GPU 等价操作。

2.8 趋同:GPU SVM 和 HMM

这种类比不是静态的------它正在趋同。现代 GPU 驱动程序(AMD KFD SVM、Intel Xe SVM)现在实现了真正的共享虚拟内存,其中:

  • GPU 和 CPU 共享相同的虚拟地址空间
  • GPU 页面故障像 CPU 页面故障一样处理
  • HMM(hmm_range_fault())桥接了这两个世界
  • 设备私有页面(MEMORY_DEVICE_PRIVATE)在 CPU 页表中表现为类似交换的条目

这种趋同验证了论点:GPU 内存子系统一直在解决与 CPU MM 相同的问题,而两者现在正通过 HMM 真正合并。


3. 架构映射

这里总结下整个架构中元素的对应关系。

CPU MM GPU MM(DRM/TTM) 结构角色
mm_struct drm_gpuvm 每进程虚拟地址空间容器
vm_area_struct drm_gpuva 连续 VA 区域描述符
struct page / folio ttm_resource / drm_gem_object 物理分配跟踪单元
物理 RAM VRAM(TTM_PL_VRAM 快速本地存储
交换空间 系统内存(TTM_PL_SYSTEM 溢出存储
页表(PGD→PTE) GPU 页表(驱动程序特定) 虚拟→物理地址转换
Buddy 分配器 drm_buddy / drm_mm 范围分配器 物理内存分配
LRU 列表 + kswapd ttm_resource_manager.lru[] + shrinker 驱逐候选者排序与回收
do_swap_page() ttm_tt_swapin() 换入/重新填充路径
PTE 中的交换条目 TTM_TT_FLAG_SWAPPED 标记内容已换出
madvise(MADV_DONTNEED) DRM_GEM_OBJECT_PURGEABLE 可回收提示
handle_mm_fault() ttm_tt_populate() / GEM 故障处理器 按需分配/填充
migrate_pages() ttm_bo_validate()(在内存间移动) 跨存储层迁移
mmu_notifier drm_gpuvm_bo_evict() 回调 映射失效通知
/proc/pid/maps debugfs GPU VA 转储 地址空间调试接口
OOM killer 驱逐失败 → -ENOMEM 资源耗尽处理

4. 为什么这种类比在架构上是必然的

这种趋同并非巧合。两个子系统解决的是相同的抽象问题

给定一个虚拟地址空间大于其快速本地内存的处理器,使用间接寻址(页表)、延迟分配(按需分页)和容量管理(驱逐/交换)在竞争的消费者之间复用有限的物理存储。

唯一的区别是:

  1. 粒度:CPU MM 以页面粒度(4KB--2MB)操作;GPU MM 通常以缓冲对象粒度(KB--GB)操作。
  2. 一致性模型:CPU 具有硬件缓存一致性;GPU 需要显式刷新/无效化或域转换。
  3. 故障延迟容忍度:CPU 页面故障会阻塞单个线程;GPU "故障"(驱逐+重新验证)在提交时批量处理(尽管现代 GPU 现在支持真正的页面故障)。
  4. 复用单位:CPU 在进程之间复用;GPU 在缓冲对象之间复用(尽管 GPUVM 现在提供每进程的 GPU VA 空间)。

5. 文档来源

5.1 主要内核文档

  • DRM 内存管理 --- 权威参考。TTM、GEM、GPUVM、DRM MM、Buddy 分配器都在这里有文档。包含 swap_storage、LRU、shrinker 和驱逐 API。

  • DRM GPUVM --- 记录了带有驱逐跟踪、分割/合并和验证的 GPU 虚拟地址空间管理器。

5.2 树内源代码(自文档化)

文件 相关类比
drivers/gpu/drm/ttm/ttm_tt.c ttm_tt_swapin()ttm_tt_swapout()swap_storage
drivers/gpu/drm/ttm/ttm_bo.c LRU 驱逐、ttm_bo_validate()
drivers/gpu/drm/ttm/ttm_pool.c 带 shrinker 的页面池(对应 slab shrinker)
drivers/gpu/drm/drm_gpuvm.c GPU VA 空间管理、驱逐列表
drivers/gpu/drm/drm_gem.c drm_gem_lru_scan() --- GEM 对象的 shrinker
include/drm/ttm/ttm_tt.h TTM_TT_FLAG_SWAPPEDstruct ttm_tt

5.3 会议演讲和文章

  • XDC(X.Org 开发者大会) --- Christian König 关于 TTM 重构的演讲明确讨论了内存层次结构和驱逐模型。
  • LWN.net --- 关于 DRM 内存管理、GPUVM(Danilo Krummrich 的系列文章,2023年)和 VM_BIND 的文章使用 CPU MM 开发者熟悉的术语讨论 GPU VA 管理。
  • "GEM - 图形执行管理器"(LWN,2008年) --- 建立 GEM 围绕 shmem 后备对象的设计理念的基础性文章。

5.4 HMM 桥梁

HMM(异构内存管理)子系统是这种类比的最终证据,因为它字面上桥接了两者

  • 设备私有页面在 CPU 页表中表现为类似交换的条目
  • hmm_range_fault() 为设备内存镜像 handle_mm_fault()
  • migrate_vma_*()migrate_pages() 扩展到设备内存

6. 结论

Linux 中的 GPU 内存管理子系统不仅仅是与 CPU 内存管理类似------它是对物理稀缺下虚拟内存管理这一根本问题的趋同重新推导。证据如下:

  1. 词汇层面 :TTM 使用 CPU MM 术语(swapswapinpopulateevictLRUshrinker)。
  2. 结构层面drm_gpuvm/drm_gpuva 在角色和实现上镜像 mm_struct/vm_area_struct
  3. 算法层面:基于 LRU 的驱逐扫描、按需填充和 shrinker 集成遵循相同的模式。
  4. 演化层面:两个子系统正通过 HMM/SVM 积极趋同,GPU 驱动程序现在直接参与 CPU MM 的页面故障和迁移基础设施。

对于内核开发者来说,理解 CPU MM 为理解 GPU 内存管理提供了直接的概念框架------反之亦然。DRM 子系统的内存管理最好被理解为不是一种新颖的设计,而是应用于不同类型处理器的经典虚拟内存理论

实际上,这也给理解TTM的设计提供了一个视角,可以从这个视角再去理解下第四章的TTM分析。


参考文献

  1. Linux 内核文档:DRM 内存管理
  2. Linux 内核源码:include/drm/ttm/ttm_tt.h
  3. Linux 内核源码:drivers/gpu/drm/ttm/ttm_tt.c
  4. Linux 内核源码:drivers/gpu/drm/drm_gpuvm.c
  5. Krummrich, D. "DRM GPUVM" --- 内核补丁和文档,2023年
  6. Corbet, J. "GEM - 图形执行管理器" --- LWN.net,2008年
  7. Linux 内核文档:异构内存管理(HMM)
相关推荐
算力百科小星2 小时前
企业AI训练算力成本结构分析:自建机房与云算力的经济性比较
gpu算力·gpu
武子康3 天前
🔥 vLLM / SGLang / TGI / TensorRT-LLM / Triton 真实分工:分层定位 + 5 步决策路径
人工智能·llm·gpu
武子康6 天前
调查研究-224 Prefill 与 Decode 分离:高并发 LLM Serving 的下一层架构
人工智能·ai·架构·llm·gpu·vllm·sglang
武子康6 天前
调查研究-223 一个请求在 vLLM 里的一生:从 HTTP 到 token streaming
人工智能·llm·gpu
styshoo8 天前
NVSentinel 数据流梳理
kubernetes·gpu·ai-infra·nvsentinel
武子康8 天前
调查研究-220 Batching 才是 GPU Serving 的第一性原理
人工智能·llm·gpu
武子康8 天前
调查研究-219 GPU 为什么能快,也为什么容易被大量小请求拖慢?
人工智能·llm·gpu
算力百科小星8 天前
GPU算力平台生态能力全景矩阵:四维生态图谱与平台适配度深度评估
gpu算力·gpu·gpu算力租用
算力百科小星9 天前
GPU算力平台可靠性评估体系:五维二十五项指标的八家主流平台量化横评
gpu算力·gpu