You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

通过P/Invoke向NVML传递含固定数组的C#结构体返回异常值

P/Invoke调用NVML库结构体数据损坏问题解决

问题描述

通过P/Invoke向NVML库函数传递结构体时出现数据损坏:结构体包含固定大小数组与unsigned long long类型数据,代码编译无错误,但返回的isRunning字段(本应仅为0或1)出现30、3等异常值,gpuUtilization值也存在异常。

NVML文档中结构体声明

NVML的nvmlAccountingStats_t结构体文档声明如下:

unsigned int  gpuUtilization
unsigned int  isRunning // 进程运行状态标记:1表示运行,0表示终止
unsigned long long  maxMemoryUsage
unsigned int  memoryUtilization
unsigned int  reserved[5] // 预留字段,供未来使用
unsigned long long  startTime // 进程启动时间(微秒级CPU时间戳)
unsigned long long  time

初始C#实现代码

以下是最小可复现的C#控制台应用代码:

// 初始C#版本的结构体定义
public struct nvmlAccountingStats_t
{
    public uint gpuUtilization;
    public uint isRunning;
    public ulong maxMemoryUsage;
    public uint memoryUtilization;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public uint[] reserved;
    public ulong startTime;
    public ulong time;
}

public enum nvmlReturn_t
{
    NVML_SUCCESS = 0,                           // 操作成功
    NVML_ERROR_UNINITIALIZED = 1,               // 未调用nvmlInit()初始化NVML
    NVML_ERROR_INVALID_ARGUMENT = 2,            // 参数无效
    NVML_ERROR_NOT_SUPPORTED = 3,               // 目标设备不支持该操作
    NVML_ERROR_NO_PERMISSION = 4,               // 当前用户无操作权限
    NVML_ERROR_ALREADY_INITIALIZED = 5,         // 已初始化(已废弃:现在允许多次初始化并引用计数)
    NVML_ERROR_NOT_FOUND = 6,                   // 查询对象失败
    NVML_ERROR_INSUFFICIENT_SIZE = 7,           // 输入参数大小不足
    NVML_ERROR_INSUFFICIENT_POWER = 8,          // 设备外部电源线未正确连接
    NVML_ERROR_DRIVER_NOT_LOADED = 9,           // NVIDIA驱动未加载
    NVML_ERROR_TIMEOUT = 10,                    // 用户提供的超时时间已过
    NVML_ERROR_IRQ_ISSUE = 11,                  // NVIDIA内核检测到GPU中断问题
    NVML_ERROR_LIBRARY_NOT_FOUND = 12,          // 无法找到或加载NVML共享库
    NVML_ERROR_FUNCTION_NOT_FOUND = 13,         // 本地NVML版本未实现该函数
    NVML_ERROR_CORRUPTED_INFOROM = 14,          // infoROM损坏
    NVML_ERROR_GPU_IS_LOST = 15,                // GPU已脱离总线或无法访问
    NVML_ERROR_RESET_REQUIRED = 16,             // GPU需要重置才能再次使用
    NVML_ERROR_OPERATING_SYSTEM = 17,           // GPU控制设备被操作系统/cgroups阻止
    NVML_ERROR_LIB_RM_VERSION_MISMATCH = 18,    // RM检测到驱动/库版本不匹配
    NVML_ERROR_IN_USE = 19,                     // GPU正忙,无法执行操作
    NVML_ERROR_MEMORY = 20,                     // 内存不足
    NVML_ERROR_NO_DATA = 21,                    // 无数据
    NVML_ERROR_VGPU_ECC_NOT_SUPPORTED = 22,     // 目标设备不支持该vgpu操作(因ECC已启用)
    NVML_ERROR_INSUFFICIENT_RESOURCES = 23,     // 关键资源耗尽(非内存)
    NVML_ERROR_FREQ_NOT_SUPPORTED = 24,         // 不支持该频率
    NVML_ERROR_UNKNOWN = 999                    // 内部驱动错误
}

[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlInit_v2();
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlShutdown();
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlDeviceGetCount_v2(out uint deviceCount);
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlDeviceGetHandleByIndex_v2(int index, out IntPtr handle);
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlDeviceGetAccountingPids(IntPtr device, ref uint count, uint[]? pids);
[DllImport(NVMLDLL)]
private static extern nvmlReturn_t nvmlDeviceGetAccountingStats(IntPtr device, uint pid, out nvmlAccountingStats_t stats);

static void Main(string[] args)
{
    if (nvmlInit_v2() == nvmlReturn_t.NVML_SUCCESS)
    {
        nvmlDeviceGetCount_v2(out uint deviceCount);
        for (int i = 0; i < deviceCount; i++)
        {
            nvmlDeviceGetHandleByIndex_v2(i, out IntPtr _device);
            // 获取进程PID列表
            uint count = 0;
            nvmlDeviceGetAccountingPids(_device, ref count, null);
            uint[] pids = new uint[count];
            if (nvmlDeviceGetAccountingPids(_device, ref count, pids) == nvmlReturn_t.NVML_SUCCESS)
            {
                foreach (uint pid in pids)
                { 
                    nvmlDeviceGetAccountingStats(_device, pid, out nvmlAccountingStats_t stats);
                    Console.WriteLine($"pid: {pid}, isRunning: {stats.isRunning}");
                }
            }
        }
        nvmlShutdown();
    }
}

异常输出结果

运行后isRunning字段出现异常值,示例输出如下:

pid: 6560, isRunning: 2
pid: 31636, isRunning: 0
pid: 30000, isRunning: 0
pid: 4164, isRunning: 0
pid: 3368, isRunning: 0
pid: 30420, isRunning: 0
pid: 732, isRunning: 0
pid: 14576, isRunning: 0
pid: 27352, isRunning: 0
pid: 25632, isRunning: 0
pid: 7352, isRunning: 0
pid: 32960, isRunning: 0
pid: 25380, isRunning: 30
pid: 21376, isRunning: 0
pid: 21396, isRunning: 0
pid: 7488, isRunning: 0
pid: 39200, isRunning: 0
pid: 15536, isRunning: 3

修正后的C#结构体

调整字段顺序并添加[StructLayout(LayoutKind.Sequential)]属性后,数据恢复正常(isRunning仅返回0或1):

[StructLayout(LayoutKind.Sequential)]
public struct nvmlAccountingStats_t
{
    public uint gpuUtilization;
    public uint memoryUtilization;
    public ulong maxMemoryUsage;
    public ulong time;
    public ulong startTime;
    public uint isRunning;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public uint[] reserved;
}

总结

  • 问题根源:C#结构体字段顺序与NVML库实际实现的C++结构体顺序不匹配,导致内存偏移错误,数据解析损坏。
  • 解决方案:确保C#结构体的字段顺序、类型与库的实际头文件定义完全一致,同时使用[StructLayout(LayoutKind.Sequential)]强制按顺序布局结构体。
  • 重要提示:不要仅依赖官方文档的结构体字段顺序,优先查看库的实际实现头文件,保证跨语言结构体布局完全对齐。

内容的提问来源于stack exchange,提问作者codingNewb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 07:03:32