CANN driver 中设备资源调度器与多进程共享机制实现
在现代人工智能基础设施中,计算设备的高效利用是核心诉求。单个高性能AI加速器往往需要同时服务于多个用户、多个进程甚至多个容器,这就对底层驱动提出了严峻挑战:如何在保障隔离性与安全性的前提下,实现计算、内存、通信等硬件资源的**公平、高效、低开销**共享?CANN(Compute Architecture for Neural Networks)的 **driver 仓库**(截至2026年初的最新
前言
在现代人工智能基础设施中,计算设备的高效利用是核心诉求。单个高性能AI加速器往往需要同时服务于多个用户、多个进程甚至多个容器,这就对底层驱动提出了严峻挑战:如何在保障隔离性与安全性的前提下,实现计算、内存、通信等硬件资源的公平、高效、低开销共享?CANN(Compute Architecture for Neural Networks)的 driver 仓库(截至2026年初的最新代码)通过一套精密的设备资源调度器(Task Resource Scheduler, TRS)与多进程共享机制,为这一问题提供了系统级的解决方案。
本文将深入剖析 CANN driver 的源码实现,从内核态的资源抽象、调度策略到用户态的共享接口,层层递进地揭示其如何支撑起复杂的多租户AI工作负载。
一、整体架构:资源调度与共享的分层设计
CANN driver 采用清晰的分层架构来管理设备资源,主要涉及三个关键层次:
- HAL 层 (Hardware Abstraction Layer):位于
src/ascend_hal,直接与硬件寄存器交互,提供最底层的资源操作原语,如任务提交、内存分配、中断处理。 - SDK-driver 层:位于
src/sdk_driver,是 HAL 能力的封装与扩展,面向用户态应用提供稳定、安全的API。资源调度器(TRS)和共享机制的核心逻辑主要集中在此层。 - DCMI 层 (DaVinci Card Management Interface):作为用户态与内核态的桥梁,通过 ioctl 和字符设备(如
/dev/davinciX)暴露标准化接口。
这种分层确保了硬件细节被有效屏蔽,上层应用无需关心具体的芯片实现,即可通过统一的接口进行资源申请与任务提交。
二、核心组件:任务资源调度器(TRS)
任务资源调度器(TRS)是 driver 中负责协调所有计算任务执行的核心引擎。它不仅要保证任务按序、正确地完成,还要在多进程并发场景下,公平地分配硬件计算单元(AICore)、内存带宽等稀缺资源。
2.1 TRS 的数据结构与状态机
TRS 的核心是一个任务队列(Task Queue),每个提交到设备的任务都会被封装成一个 trs_task 结构体,并进入队列等待调度。
// src/sdk_driver/trs/trs_task.h
struct trs_task {
uint64_t task_id; // 全局唯一任务ID
pid_t submit_pid; // 提交此任务的进程PID
struct file *submit_file; // 提交此任务的文件描述符(用于权限校验)
void *sq_vaddr; // 提交队列(Submit Queue)虚拟地址
uint32_t sq_size; // 提交队列大小
enum trs_task_state state; // 任务状态:PENDING, RUNNING, COMPLETED, ERROR
struct list_head list; // 链入全局任务队列
// ... 其他字段
};
关键点在于 submit_pid 和 submit_file 字段。它们记录了任务的“归属”,是后续实现进程级资源隔离与回收的关键。
2.2 调度流程:从用户提交到硬件执行
- 用户提交:用户态应用(如 PyTorch)通过 CANN Runtime 调用
aclrtEnqueue等API,最终会通过 DCMI 接口向内核 driver 发送一个包含任务描述符的 ioctl 命令。 - 内核接收与封装:driver 的
sdk_driver/ts_agent模块接收到请求后,会创建一个trs_task对象,填充submit_pid(来自current->pid)和submit_file(来自 ioctl 的struct file *参数),并将其加入 TRS 的全局待处理队列。 - 调度器轮询:TRS 的主调度线程(通常是一个内核线程)会周期性地扫描任务队列。对于
PENDING状态的任务,它会检查硬件资源是否可用。 - 硬件派发:一旦资源就绪,调度器会将任务描述符写入硬件的命令队列(Command Queue),并触发硬件开始执行。
- 完成通知:硬件执行完毕后,会触发中断。driver 的中断处理程序会将对应
trs_task的状态更新为COMPLETED,并通过 eventfd 或其他机制通知用户态。
这个流程确保了所有任务都经过一个中心化的调度点,为实现公平性和优先级调度奠定了基础。
2.3 公平性与优先级
虽然开源代码中未完全暴露复杂的调度策略,但从 trs_task 结构和相关代码可以推断,TRS 支持基于进程或用户的简单公平调度。例如,调度器可以维护一个 per-PID 的任务计数器,防止单个进程独占所有硬件资源。
三、多进程共享机制:从设备文件到虚拟化
多进程共享是 CANN driver 的另一大核心能力。它允许多个独立的进程(甚至来自不同用户)安全地访问同一物理设备。
3.1 设备文件 (/dev/davinciX) 的角色
每个物理计算设备在 Linux 系统中被注册为一个字符设备,例如 /dev/davinci0。这是多进程共享的入口点。
- 打开 (
open):当一个进程首次打开/dev/davinci0时,driver 会为其在内核中创建一个设备上下文(Device Context),记录该进程与此设备的关联关系。 - 权限控制:Linux 的标准文件权限(
chmod,chown)可以直接应用于/dev/davinciX,从而控制哪些用户或组可以访问设备,实现了基础的安全隔离。
3.2 进程级资源追踪与自动回收
这是多进程共享中最关键也最易被忽视的一环:当一个进程异常退出(crash)或被杀死(kill)时,它所占用的所有设备资源(如分配的内存、提交但未完成的任务)必须被自动、彻底地回收,否则会导致资源泄漏,最终使整个设备不可用。
CANN driver 通过 Linux 内核的 mmu_notifier 机制和 file_operations 的 release 回调 来实现这一点。
关键机制一:file_operations->release
每个打开 /dev/davinciX 的进程都有一个对应的 struct file。当进程退出时,内核会自动调用该 file 的 release 回调函数。
// src/sdk_driver/driver_main.c
static int davinci_release(struct inode *inode, struct file *file)
{
struct ascend_client *client = file->private_data;
if (!client) return 0;
// 1. 清理该client提交的所有未完成任务
trs_cleanup_tasks_by_client(client);
// 2. 释放该client分配的所有设备内存
svm_cleanup_all_by_client(client);
// 3. 销毁client上下文
kfree(client);
file->private_data = NULL;
return 0;
}
trs_cleanup_tasks_by_client 函数会遍历 TRS 的全局任务队列,找到所有 submit_file == file 的任务,并强制将其标记为 ERROR 状态,然后释放相关资源。这确保了“孤儿”任务不会永远阻塞调度器。
关键机制二:共享虚拟内存(SVM)的进程绑定
CANN driver 使用 SVM(Shared Virtual Memory)技术,允许 CPU 和 NPU 直接共享同一块虚拟地址空间,极大简化了数据搬运。SVM 内存的分配同样与进程绑定。
// src/sdk_driver/svm/svm_manager.c
struct svm_region {
uint64_t vaddr;
size_t size;
struct pid *owner_pid; // 记录内存所有者的PID
struct list_head list;
};
int svm_alloc(struct file *file, uint64_t *vaddr, size_t size)
{
struct svm_region *region = kzalloc(sizeof(*region), GFP_KERNEL);
region->vaddr = *vaddr;
region->size = size;
region->owner_pid = get_pid(task_tgid(current)); // 绑定到当前进程
// ... 添加到全局SVM列表
}
在 davinci_release 中调用的 svm_cleanup_all_by_client 会遍历所有 SVM 区域,释放 owner_pid 与当前退出进程 PID 匹配的内存块。
3.3 容器化支持:device-share 特性
driver 仓库在近期(PR !35)引入了对容器环境的深度优化——device-share 特性。它允许在 Docker 或 Kubernetes 环境中,通过挂载设备文件的方式,让多个容器共享同一个物理设备。
其核心在于,driver 能够正确识别容器内的进程(通过 cgroup 信息),并将资源回收逻辑正确应用到容器粒度。虽然具体实现细节较为复杂,但其思想与上述的进程级回收一脉相承,只是将“进程”概念扩展到了“容器沙箱”。
用户可以通过以下命令验证:
$ npu-smi info -t device-share -i 0 -c 0
Device-share Status : True
这表明 driver 已成功识别并启用了设备共享模式。
四、通信与同步:HDC 与 Event 机制
在多进程共享场景下,进程间可能需要协同工作,这就需要高效的通信与同步机制。CANN driver 提供了 HDC(Host-Device Communication)。
- Mailbox:一种轻量级的、基于内存的消息传递机制,用于在 Host CPU 和 Device 之间交换控制信息。
- Event:一种异步通知机制。用户态可以创建一个 event 对象,并将其与一个任务关联。当任务完成时,driver 会自动触发该 event,用户态进程可以通过
poll或epoll等待事件发生,而无需忙询。
这些机制由 src/ascend_hal/hdc 和 src/sdk_driver/hdc 模块实现,为上层构建复杂的分布式训练或推理流水线提供了基础。
结语
CANN driver 中的设备资源调度器与多进程共享机制,是一套融合了操作系统原理、硬件架构知识和软件工程实践的精密系统。它通过 TRS 实现了对计算任务的有序、公平调度;通过深度集成 Linux 内核的 file 生命周期管理和 mmu_notifier 机制,实现了进程级资源的自动追踪与安全回收;并通过 device-share 等特性,无缝适配了现代云原生容器化环境。这套机制不仅是 CANN 软件栈稳定、高效运行的基石,也为构建大规模、多租户的 AI 计算平台提供了坚实的底层保障。
cann组织链接:https://atomgit.com/cann
driver仓库链接:https://atomgit.com/cann/driver
更多推荐


所有评论(0)