0.2 ROCm 软件栈全景:libhsakmt 在 rocm 中的位置

在深入 libhsakmt 的逐模块分析之前,我们需要先建立一个全局视角:ROCm 软件栈的整体架构是什么样的?libhsakmt 究竟处于哪一层?它与上层的 HSA Runtime、下层的 KFD 内核驱动之间是什么关系?本篇将回答这些问题,为后续的深度剖析奠定架构认知基础。


1. ROCm 软件栈分层架构

ROCm(Radeon Open Compute)是 AMD 面向 GPU 通用计算的开源软件平台。其软件栈采用严格的分层设计,从上到下可划分为五个层次:

+---------------------------------------------------+
|            User App / AI Framework / HPC          |
+---------------------------------------------------+
|        Programming Model: HIP / OpenCL / OpenMP   |
+---------------------------------------------------+
|    HSA Runtime (libhsa-runtime64.so) -- ROCr      |
+---------------------------------------------------+
|    Thunk Layer (libhsakmt) -- ROCt                |
+---------------------------------------------------+
|    KFD Kernel Driver (/dev/kfd) -- ROCk           |
+---------------------------------------------------+
|    AMDGPU DRM Driver + GPU Hardware               |
+---------------------------------------------------+

第一层:用户应用。 PyTorch、TensorFlow、各类 HPC 科学计算程序,它们通过 HIP 或 OpenCL 等编程接口使用 GPU。

第二层:编程模型层。 HIP 是 AMD 提供的 GPU 编程接口(类似 CUDA),负责将 hipMallochipLaunchKernel 等高层调用翻译为 HSA Runtime API。OpenCL 运行时也位于这一层。

第三层:HSA Runtime(ROCr)。 AMD 对 HSA(Heterogeneous System Architecture)标准的实现,以 libhsa-runtime64.so 的形式交付。它提供标准的 hsa_*() 系列 C API,负责 GPU 代理(Agent)管理、内存区域抽象、AQL 队列调度、信号同步、代码对象加载等核心功能。

第四层:Thunk 层(ROCt)。 即本专栏的主角 ——libhsakmt。它是用户态与内核态之间的"翻译层",将 HSA Runtime 的操作请求转换为对 /dev/kfd 设备节点的 ioctl 系统调用。

第五层:KFD 内核驱动(ROCk)。 运行在内核态的 AMD GPU 计算驱动,负责硬件队列的实际创建、GPU 页表的管理、中断处理等底层硬件操作。


2. libhsakmt 的定位:用户态与内核态的桥梁

libhsakmt 的全称是 HSA Kernel Mode Thunk,“Thunk"一词源自编译器术语,意为"转换层"或"适配层”。它的核心职责可以用一句话概括:

将用户态的 GPU 资源管理请求,翻译为内核态 KFD 驱动能理解的 ioctl 调用。

具体来说,当上层 HSA Runtime 需要创建一个 GPU 队列时,它不会直接操作 /dev/kfd,而是调用 libhsakmt 提供的 hsaKmtCreateQueue() 函数。libhsakmt 负责:

  1. 将参数组装为 KFD ioctl 所需的数据结构;
  2. 通过 ioctl() 系统调用发送给内核驱动;
  3. 处理返回值,将内核态的错误码转换为用户态的状态码。

这种设计的好处是:上层 Runtime 不需要关心内核接口的细节变化,libhsakmt 作为中间层承担了版本适配与接口稳定的责任。

熟悉 Linux 图形栈的朋友可以这样理解:libdrm 是图形渲染栈(Mesa / Vulkan)与 DRM 内核驱动之间的用户态桥梁,libhsakmt 在 GPU 计算栈中扮演的角色与之完全对等 —— 它是 HSA Runtime 与 KFD 内核驱动之间的用户态桥梁。两者都通过 ioctl 与各自的内核驱动通信,只是一个面向图形,一个面向计算。


3. libhsakmt 与 HSA Runtime 的构建关系

一个容易产生困惑的问题是:libhsakmt 是一个独立的共享库吗?

答案是:不是。 在当前的构建体系中,libhsakmt 始终被编译为静态库,然后链接进 libhsa-runtime64.so。最终交付给用户的只有一个库文件 libhsa-runtime64.so,libhsakmt 的代码已经嵌入其中。

从构建系统的角度看:

rocr-runtime/
├── libhsakmt/          → 编译为 libhsakmt.a(静态库)
└── runtime/
    └── hsa-runtime/    → 编译为 libhsa-runtime64.so(链接 libhsakmt.a)

这意味着,虽然 libhsakmt 在源码层面是一个独立模块,但在运行时它是 HSA Runtime 的一部分。用户的应用程序只需链接 libhsa-runtime64.so,即可间接使用 libhsakmt 的所有功能。


4. HSA Runtime 如何调用 libhsakmt

HSA Runtime 内部通过一个 Driver 抽象层 来访问 libhsakmt。核心类层次如下:

core::Driver(抽象基类)
    ├── AMD::KfdDriver      → 调用 hsaKmt*() 函数(Linux 原生路径)
    ├── AMD::XdnaDriver     → 面向 AI Engine(NPU)的驱动后端
    └── AMD::VirtioDriver   → 面向虚拟化环境的 VirtIO 后端

KfdDriver 是最核心的实现,它将 Runtime 层的操作逐一映射到 libhsakmt 的 API:

HSA Runtime 操作KfdDriver 调用的 libhsakmt API功能
发现 GPU 设备hsaKmtOpenKFD + hsaKmtAcquireSystemProperties打开 KFD 并获取系统拓扑
查询节点属性hsaKmtGetNodeProperties / GetNodeMemoryProperties获取 GPU 核心数、显存大小等
创建调度队列hsaKmtCreateQueue创建硬件命令队列
分配 GPU 内存hsaKmtAllocMemory + hsaKmtMapMemoryToGPU分配并映射显存
同步等待hsaKmtCreateEvent + hsaKmtWaitOnEvent创建硬件事件并等待完成
性能采集hsaKmtPmcStartTrace / hsaKmtPmcQueryTrace启动 / 查询硬件性能计数器

这种 Driver 抽象的设计使得 Runtime 能够支持多种后端:标准 KFD 路径、虚拟化 VirtIO 路径、甚至 AI 引擎(XDNA)路径,而上层代码无需修改。


5. 一次 hipMalloc 的完整调用链路

为了更直观地理解 libhsakmt 的位置,我们追踪一次 hipMalloc() 调用从用户代码到硬件的完整路径:

hipMalloc(&ptr, size)
    |
    v
HIP Runtime: parse args, select target GPU
    |
    v
HSA Runtime (hsa_amd_memory_pool_allocate):
    |  create alloc request, select MemoryRegion
    v
KfdDriver::AllocateMemory():
    |  call libhsakmt hsaKmtAllocMemory()
    v
libhsakmt (memory.c -> fmm.c):
    |  assemble ioctl params, manage Aperture VA space
    |  ioctl(fd, AMDKFD_IOC_ALLOC_MEMORY_OF_GPU, ...)
    v
KFD Kernel Driver:
    |  alloc physical VRAM, setup GPU page table mapping
    v
AMDGPU DRM + GPU Hardware: actual VRAM allocation

从这条链路可以清晰地看到:libhsakmt 是 用户态最后一道关卡,它之下就是内核态的 ioctl 调用。所有 GPU 资源操作的"最后一公里",都由 libhsakmt 来完成。


6. HSA Runtime 的核心子系统(libhsakmt 之上)

理解 libhsakmt 之上的 HSA Runtime 子系统,有助于把握 libhsakmt 各模块被调用的场景:

Runtime 子系统职责依赖的 libhsakmt 模块
Topology(拓扑发现)枚举 CPU / GPU Agent,构建设备连接图OpenClose、Topology
Agent(设备代理)封装 GPU/CPU 节点的属性与能力Topology
Memory Region(内存区域)抽象 GPU 显存、系统内存、LDS 等内存池Memory、FMM
AQL Queue(队列调度)管理 HSA AQL 格式的命令队列Queue
Signal(信号系统)实现硬件/软件信号与 IPC 信号Event
Blit Engine(拷贝引擎)SDMA 和 Kernel 两种 GPU 内存拷贝路径Queue、Memory
Code Loader(代码加载器)加载 GPU ELF 代码对象,解析符号与重定位Memory
PC Sampling(采样扩展)GPU 程序计数器采样PC Sampling

7. 小结

将本篇的核心信息浓缩为三句话:

  1. libhsakmt 是 ROCm 软件栈中用户态与内核态的分界线 —— 它之上是纯用户态的 C++ 抽象(HSA Runtime),它之下是通过 ioctl 进入内核的系统调用。

  2. libhsakmt 以静态库形式嵌入 libhsa-runtime64.so —— 源码独立,运行时合一。HSA Runtime 通过 Driver 抽象层调用 libhsakmt 的 hsaKmt*() 系列 API。

  3. 理解 libhsakmt 就是理解 ROCm GPU 资源管理的本质 —— 拓扑发现、内存分配、队列创建、事件同步、性能采集,所有硬件资源操作最终都汇聚于此。

后续各章将逐一深入 libhsakmt 的每个模块,从 OpenClose 的设备握手开始,到 VirtIO 虚拟化后端收尾,带你走完这条从硬件到用户态的完整技术链路。

「LLM那些事」系列第 4 篇《上下文窗口的边界》,文章连接:https://blog.csdn.net/houwenjin/article/details/163999753。 演示什么:在「预测」Sheet 的黄色格子里输入一句话(默认「来泡一杯」),四个「模型」——分别只统计最后 1 / 2 / 3 / 4 个字的 n-gram 查表——同时预测下一个字。同一个输入,看的上下文越长,候选越少、预测越确定: ┌────────────────┬──────────┬───────────────┬──────┐ │ 只看最后几个字 │ 用的前缀 │ 候选下一字数 │ 预测 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 1 个 │ 杯 │ 3(茶/子/水) │ 模糊 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 2 个 │ 一杯 │ 2(茶/水) │ 收窄 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 3 个 │ 泡一杯 │ 1(茶) │ 确定 │ ├────────────────┼──────────┼───────────────┼──────┤ │ 4 个 │ 来泡一杯 │ 1(茶) │ 确定 │ └────────────────┴──────────┴───────────────┴──────┘
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值