昇腾NPU的驱动程序,NPU和CPU之间的桥梁
前言
昇腾NPU插在主板上,PyTorch能直接调用它跑模型,这个过程是怎么实现的?驱动扮演了什么角色?为什么驱动版本不对,NPU就跑不起来?
第一次研究昇腾驱动的时候,也被它的分层架构搞得很懵。从PyTorch调用到NPU执行,中间经过了哪些层?每层负责什么?为什么有时候"驱动装对了,但NPU还是不可用"?
带着这个疑问,深入研究了driver仓库的源码,发现driver不是简单的"硬件驱动",而是一个分层软件栈,包括HDF(Hardware Driver Foundation)、HDK(Hardware Development Kit)、Runtime Library三层,每层各司其职,共同完成"让PyTorch能用NPU"这个目标。
本文是概念拆解——会拆开driver的分层架构、核心模块、常见问题,解释为什么driver是昇腾CANN的基础层,以及为什么它出问题会影响整个栈。
driver在CANN五层架构里的位置
先说清楚driver住在哪。昇腾CANN的架构分五层,driver住在第5层——昇腾计算基础层,是整个栈的最底层。
第1层:昇腾计算语言层 AscendCL
└─ 算子开发接口 Ascend C
第2层:昇腾计算服务层
├─ AOL 算子库
├─ AOE 调优引擎
└─ Framework Adaptor 框架适配器
第3层:昇腾计算编译层
├─ Graph Compiler 图编译器
└─ BiSheng / ATC 编译器
第4层:昇腾计算执行层
├─ Runtime 运行时
├─ Graph Executor 图执行器
├─ HCCL 集合通信库
└─ AIPP / DVPP
第5层:昇腾计算基础层 ← driver 住在这
├─ RMS(Resource Management Service)
├─ CMS(Configuration Management Service)
├─ DMS(Device Management Service)
├─ DRV(DRiVer,驱动核心)
└─ UTILITY
硬件层:昇腾 AI 硬件(达芬奇架构)
为啥住第5层?因为driver是"硬件抽象层",是连接软件栈和物理硬件的桥梁。没有driver,软件栈就不知道硬件长什么样,更不知道怎么控制硬件。
依赖关系
driver → hardware(NPU)。driver直接控制NPU硬件,是整个栈的最底层。上层的HCCS、Runtime、AscendCL都依赖driver提供的接口。
分层架构:driver的三层结构
driver不是一层代码,而是三层代码:HDF层、HDK层、Runtime Library层。
第1层:HDF(Hardware Driver Foundation)
HDF是驱动框架层,定义了驱动的标准框架和接口规范。所有的昇腾驱动都要遵循HDF规范,这样才能被上层软件统一管理。
核心模块:
// HDF核心:设备管理
class HdfDeviceManager {
public:
// 加载设备驱动
static int LoadDevice(const char* device_name);
// 卸载设备驱动
static int UnloadDevice(const char* device_name);
// 获取设备句柄
static void* GetDevice(const char* device_name);
};
// HDF核心:驱动服务
class HdfDriverService {
public:
// 初始化驱动服务
virtual int Initialize() = 0;
// 销毁驱动服务
virtual int Release() = 0;
// 绑定设备
virtual int BindDevice(struct Device* device) = 0;
// 解绑设备
virtual int UnbindDevice(struct Device* device) = 0;
};
// NPU驱动服务
class NpuDriverService : public HdfDriverService {
public:
int Initialize() override;
int Release() override;
int BindDevice(struct Device* device) override;
int UnbindDevice(struct Device* device) override;
// NPU特有接口
int SubmitTask(struct Task* task);
int QueryTask(uint64_t task_id);
int SyncDevice();
};
关键点:
HdfDeviceManager:设备管理器,负责加载/卸载设备驱动HdfDriverService:驱动服务基类,定义了驱动的生命周期接口NpuDriverService:NPU驱动服务,实现了NPU特有的接口
踩坑预警:HDF层是内核态代码,普通开发者不要直接修改,看看就好。
第2层:HDK(Hardware Development Kit)
HDK是硬件开发工具包,提供了驱动开发的常用工具和接口。开发者基于HDK,可以快速开发新的驱动或者扩展现有驱动。
核心模块:
// HDK核心:内存管理
class HdkMemory {
public:
// 分配设备内存
static void* AllocDeviceMem(size_t size);
// 释放设备内存
static void FreeDeviceMem(void* ptr);
// 分配Host内存(锁页内存,用于DMA)
static void* AllocHostMem(size_t size);
// 释放Host内存
static void FreeHostMem(void* ptr);
// 内存拷贝(Device ↔ Host)
static void Memcpy(void* dst, const void* src, size_t size);
};
// HDK核心:任务调度
class HdkTask {
public:
// 创建任务
static uint64_t CreateTask(struct TaskConfig* config);
// 提交任务
static int SubmitTask(uint64_t task_id);
// 等待任务完成
static int WaitTask(uint64_t task_id, uint32_t timeout_ms);
// 取消任务
static int CancelTask(uint64_t task_id);
};
// HDK核心:事件管理
class HdkEvent {
public:
// 创建事件
static uint64_t CreateEvent();
// 等待事件
static int WaitEvent(uint64_t event_id, uint32_t timeout_ms);
// 触发事件
static int SignalEvent(uint64_t event_id);
// 销毁事件
static int DestroyEvent(uint64_t event_id);
};
关键点:
HdkMemory:内存管理,封装了设备内存、Host内存、DMA拷贝的接口HdkTask:任务调度,封装了任务创建、提交、等待、取消的接口HdkEvent:事件管理,封装了事件创建、等待、触发、销毁的接口
踩坑预警:HDK层是内核态代码,普通开发者不要直接修改,看看就好。
第3层:Runtime Library(用户态驱动接口)
Runtime Library是用户态驱动接口,是普通开发者接触driver的唯一途径。PyTorch、AscendCL、Runtime都通过Runtime Library和driver交互。
核心模块:
// Runtime Library核心:设备管理
class DeviceManager {
public:
// 初始化设备管理
static int Init();
// 销毁设备管理
static int Destroy();
// 获取设备数量
static int GetDeviceCount();
// 设置当前设备
static int SetDevice(int device_id);
// 获取当前设备
static int GetDevice();
// 同步设备
static int SyncDevice();
};
// Runtime Library核心:内存管理
class MemoryManager {
public:
// 分配设备内存
static void* Alloc(size_t size);
// 释放设备内存
static void Free(void* ptr);
// 分配锁页内存(用于DMA)
static void* AllocPinned(size_t size);
// 释放锁页内存
static void FreePinned(void* ptr);
// 内存拷贝(同步)
static void Memcpy(void* dst, const void* src, size_t size);
// 内存拷贝(异步)
static void MemcpyAsync(void* dst, const void* src, size_t size, Stream stream);
};
// Runtime Library核心:流管理
class StreamManager {
public:
// 创建流
static Stream CreateStream();
// 销毁流
static int DestroyStream(Stream stream);
// 同步流
static int SyncStream(Stream stream);
// 等待流
static int StreamWait(Stream stream, Event event);
};
关键点:
DeviceManager:设备管理,初始化driver、设置当前设备、同步设备MemoryManager:内存管理,分配/释放设备内存、锁页内存、DMA拷贝StreamManager:流管理,创建/销毁/同步流
踩坑预警:Runtime Library是用户态代码,可以直接调用,但要确保driver版本匹配。
核心能力:driver到底能干啥?
driver的核心能力分4类:设备管理、内存管理、任务调度、中断处理。
1. 设备管理
设备管理是"让软件知道NPU存在"。driver负责枚举NPU设备、加载驱动、初始化设备。
代码讲解:
import acl
# 初始化ACL(Ascend Computing Language)
acl.init()
# 获取设备数量
device_count = acl.get_device_count()
print(f"NPU设备数量: {device_count}")
# 分配设备
device_id = 0
acl.rt.set_device(device_id)
# 释放设备
acl.rt.reset_device(device_id)
# 销毁ACL
acl.finalize()
代码讲解:
acl.init():初始化ACL,加载driveracl.get_device_count():获取NPU设备数量acl.rt.set_device():分配设备acl.rt.reset_device():释放设备
踩坑预警:分配设备后一定要释放,不然会资源泄漏。
2. 内存管理
内存管理是"让软件能往NPU上写数据"。driver负责分配设备内存、Host内存、DMA拷贝。
代码讲解:
import numpy as np
import acl
# 分配设备内存
size = 1024 * 4 # 1024个float32
ptr = acl.rt.malloc(size, acl.RT_MEM_MALLOC_NORMAL_ONLY)
print(f"设备内存地址: {hex(ptr)}")
# 从numpy数组拷贝数据到设备
host_data = np.random.randn(1024).astype(np.float32)
acl.rt.memcpy(ptr, size, host_data.ctypes.data, size, acl.RT_MEMCPY_HOST_TO_DEVICE)
# 从设备拷贝数据回numpy数组
result = np.empty(1024, dtype=np.float32)
acl.rt.memcpy(result.ctypes.data, size, ptr, size, acl.RT_MEMCPY_DEVICE_TO_HOST)
print(f"结果: {result[:5]}")
# 释放设备内存
acl.rt.free(ptr)
代码讲解:
acl.rt.malloc():分配设备内存acl.rt.memcpy():DMA拷贝(H2D或D2H)acl.rt.free():释放设备内存
踩坑预警:设备内存要手动释放,不然会内存泄漏。
3. 任务调度
任务调度是"让软件能往NPU上提交计算任务"。driver负责创建任务、提交任务、等待任务完成。
代码讲解:
import acl
# 创建流
stream, ret = acl.rt.create_stream()
print(f"流创建成功: {stream}")
# 创建任务(以VectorAdd为例)
task_config = {
"op_type": "VectorAdd",
"input_x": ptr_x,
"input_y": ptr_y,
"output_z": ptr_z,
"count": 1024
}
task_id = acl.rt.launch_task(stream, task_config)
print(f"任务提交成功: {task_id}")
# 等待任务完成
acl.rt.stream_synchronize(stream)
print(f"任务执行完成")
# 销毁流
acl.rt.destroy_stream(stream)
代码讲解:
acl.rt.create_stream():创建流acl.rt.launch_task():提交任务acl.rt.stream_synchronize():等待流完成
踩坑预警:提交任务后一定要等待完成,不然结果还没算出来就读了。
4. 中断处理
中断处理是"让NPU能主动通知CPU"。driver负责注册中断处理函数、处理中断、唤醒等待的CPU核。
代码讲解:
import acl
# 注册中断回调函数
def interrupt_callback(interrupt_type, task_id, user_data):
print(f"中断类型: {interrupt_type}, 任务ID: {task_id}")
# 处理中断...
# 注册中断回调
acl.rt.register_interrupt_callback(interrupt_callback, None)
# 提交一个需要中断通知的任务
task_id = acl.rt.launch_task_with_interrupt(stream, task_config)
# CPU继续做其他事情,等中断唤醒
print("CPU继续做其他事情...")
# 等NPU发来中断...
代码讲解:
acl.rt.register_interrupt_callback():注册中断回调函数acl.rt.launch_task_with_interrupt():提交需要中断通知的任务
踩坑预警:中断回调函数要尽快返回,不然会影响其他中断。
常见问题:driver出错了怎么办?
问题1:驱动版本不匹配
现象:运行acl.init(),报错说driver version mismatch。
原因:CANN版本和driver版本不匹配。
解决:去昇腾社区下载和CANN版本匹配的driver。
# 查看当前driver版本
cat /usr/local/Ascend/driver/version.info
# 查看CANN版本
atc --version
# 如果版本不匹配,重新安装匹配的driver
bash ascend-installer-8.0.run --full --driver
问题2:设备不可用
现象:运行acl.rt.set_device(0),报错说device not found。
原因:driver没加载,或者NPU硬件故障。
解决:检查driver加载状态和NPU硬件状态。
# 检查driver是否加载
lsmod | grep ascend
# 如果没加载,加载driver
modprobe ascend_driver
# 检查NPU硬件状态
npu-smi info
# 如果NPU硬件故障,重启机器或者联系售后
问题3:内存分配失败
现象:运行acl.rt.malloc(),报错说out of memory。
原因:设备内存不足,或者内存碎片太多。
解决:释放不必要的内存,或者重启进程。
# 检查设备内存使用情况
mem_info = acl.rt.get_mem_info(acl.RT_MEM_INFO_TOTAL)
print(f"设备总内存: {mem_info['total'] / 1024**3:.2f} GB")
mem_info = acl.rt.get_mem_info(acl.RT_MEM_INFO_USED)
print(f"设备已用内存: {mem_info['used'] / 1024**3:.2f} GB")
# 释放不必要的内存
del large_tensor # Python层面释放
acl.rt.reset_device(0) # 重置设备,释放所有设备内存
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)