1. NVLink拓扑发现与设备枚举
任务目标
编写一个C语言程序,枚举系统中所有NVLink设备(GPU与DPU),识别设备类型,并输出设备间的NVLink连接拓扑。程序通过调用NVIDIA Management Library (NVML) API实现设备发现与链路查询,最终以文本形式打印拓扑结构。
环境依赖
- NVIDIA驱动(支持NVML)已安装
- NVML开发头文件(通常位于
/usr/local/cuda/include/nvml.h) - 链接库:
-lnvml - 编译环境:gcc
关键完整代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <nvml.h>
#define MAX_DEVICES 64
#define MAX_LINKS 18
typedef struct {
nvmlDevice_t handle;
char uuid[NVML_DEVICE_UUID_BUFFER_SIZE];
char name[NVML_DEVICE_NAME_BUFFER_SIZE];
int isDPU;
int linkCount;
int linkDevices[MAX_LINKS];
} DeviceInfo;
static int is_dpu(nvmlDevice_t dev) {
nvmlFieldValue_t val;
val.fieldId = NVML_FI_DEVICE_TYPE;
if (nvmlDeviceGetFieldValues(dev, 1, &val) != NVML_SUCCESS)
return 0;
return (val.value.uiVal == NVML_DEVICE_TYPE_DPU);
}
int main() {
if (nvmlInit() != NVML_SUCCESS) {
fprintf(stderr, "NVML初始化失败,请检查驱动\n");
return 1;
}
unsigned int devCount = 0;
if (nvmlDeviceGetCount(&devCount) != NVML_SUCCESS || devCount == 0) {
fprintf(stderr, "未发现任何NVML设备\n");
nvmlShutdown();
return 1;
}
if (devCount > MAX_DEVICES) devCount = MAX_DEVICES;
DeviceInfo devices[MAX_DEVICES];
memset(devices, 0, sizeof(devices));
// 枚举所有设备
for (unsigned int i = 0; i < devCount; i++) {
nvmlDevice_t dev;
if (nvmlDeviceGetHandleByIndex(i, &dev) != NVML_SUCCESS)
continue;
devices[i].handle = dev;
nvmlDeviceGetUUID(dev, devices[i].uuid, NVML_DEVICE_UUID_BUFFER_SIZE);
nvmlDeviceGetName(dev, devices[i].name, NVML_DEVICE_NAME_BUFFER_SIZE);
devices[i].isDPU = is_dpu(dev);
}
// 查询NVLink连接关系
for (unsigned int i = 0; i < devCount; i++) {
if (devices[i].handle == NULL) continue;
for (unsigned int link = 0; link < NVML_NVLINK_MAX_LINKS; link++) {
nvmlPciInfo_t pci;
if (nvmlDeviceGetNvLinkRemotePciInfo(devices[i].handle, link, &pci) == NVML_SUCCESS) {
// 通过PCI bus ID匹配远端设备
for (unsigned int j = 0; j < devCount; j++) {
if (j == i || devices[j].handle == NULL) continue;
nvmlPciInfo_t localPci;
nvmlDeviceGetPciInfo(devices[j].handle, &localPci);
if (strcmp(pci.busId, localPci.busId) == 0) {
devices[i].linkDevices[devices[i].linkCount++] = j;
break;
}
}
}
}
}
// 输出拓扑
printf("=== NVLink拓扑发现结果 ===\n");
printf("发现设备总数: %u\n\n", devCount);
for (unsigned int i = 0; i < devCount; i++) {
if (devices[i].handle == NULL) continue;
printf("[%u] %s\n", i, devices[i].name);
printf(" UUID: %s\n", devices[i].uuid);
printf(" 类型: %s\n", devices[i].isDPU ? "DPU" : "GPU");
printf(" NVLink连接: ");
if (devices[i].linkCount == 0) {
printf("无\n");
} else {
for (int k = 0; k < devices[i].linkCount; k++) {
int peer = devices[i].linkDevices[k];
printf("%s[%u](%s)%s",
k == 0 ? "" : ", ",
peer,
devices[peer].name,
k == devices[i].linkCount - 1 ? "\n" : "");
}
}
}
nvmlShutdown();
return 0;
}
编译与运行
gcc -o nvlink_topology nvlink_topology.c -lnvml
sudo ./nvlink_topology
需要root权限以访问NVML设备信息。
验证方法
程序运行后,观察输出是否满足以下条件即判定通过:
- 输出设备总数与
nvidia-smi中看到的GPU数量一致(若系统无DPU,则全部显示为GPU) - 每台设备均显示正确的类型(GPU或DPU)
- 存在NVLink连接的设备对,在彼此的连接列表中互相可见(例如设备0连接列表包含设备1,设备1连接列表也包含设备0)
- 对于无NVLink连接的设备,显示“无”
可通过 nvidia-smi nvlink -s 命令交叉验证实际链路连接关系,程序输出的连接对应与之一致。
常见问题
- NVML初始化失败:确认驱动已安装且版本匹配,运行
nvidia-smi能正常输出 - 编译找不到nvml.h:确认CUDA工具包已安装,必要时添加
-I/usr/local/cuda/include - 链接错误:确认
libnvml.so在库搜索路径中,可添加-L/usr/local/cuda/lib64 - DPU识别不准确:部分早期DPU可能不支持
NVML_FI_DEVICE_TYPE字段,此时会误判为GPU,可结合设备名称关键字(如包含“BlueField”)辅助判断