Windows 显卡到 \Device\VideoXX 的精确映射方法

0 阅读6分钟

概述

在 Windows 系统中,当 NVIDIA 驱动报告 FECS (Falcon Error and Control System) 错误时,事件日志通常会包含类似 \Device\Video10 的设备标识。然而,Windows 没有提供直接的 API 来将这个内核设备路径映射到具体的物理显卡。

本文档描述了一种 100% 精确 的映射方法,通过注册表查询和 GUID 匹配,实现从 \Device\VideoXX 到 PnP 实例标识符的精确定位。


问题背景

典型场景

  • 系统有 4 块 NVIDIA RTX 3090 显卡
  • FECS 错误事件包含设备信息:\Device\Video10
  • 需要定位出错的是哪一块物理显卡以执行重置

传统方法的局限性

  1. 匹配设备标识不精确:注册表中的 MatchingDeviceId 只包含硬件型号信息(如 pci\ven_10de&dev_2204),会匹配所有同型号显卡
  2. 适配器索引映射混乱:同一物理显卡可能对应 4 个不同的 Video 设备(多显示输出)
  3. 显卡标识无法反查:NVIDIA 驱动的 RMGpuId 是唯一的,但 PnP 设备端没有对应字段

解决方案:通过视频标识GUID 精确匹配

核心发现

每个物理显卡在注册表中有一个 唯一的视频标识GUID,存储在:

HKLM:\SYSTEM\CurrentControlSet\Enum\<显卡实例标识>\Device Parameters\VideoID

同时,每个 \Device\VideoXX 设备的注册表路径中也包含一个 Video GUID,可以从 HARDWARE\DEVICEMAP\VIDEO 查询获得。

这两个 GUID 完全匹配,从而实现精确映射。


映射流程

步骤 1:从事件日志提取 Video 编号

# FECS 错误事件 (Event ID 13, nvlddmkm)
$设备路径 = "\Device\Video10"
$设备路径 -match 'Video(\d+)'
$视频编号 = [int]$Matches[1]  # 10

步骤 2:查询 HARDWARE\DEVICEMAP\VIDEO

$设备映射表 = Get-ItemProperty 'HKLM:\HARDWARE\DEVICEMAP\VIDEO'
$视频键名 = "\Device\Video$视频编号"
$注册表路径 = $设备映射表.$视频键名
# 结果: \Registry\Machine\System\CurrentControlSet\Control\Video\{73430590-0080-11F0-8FFC-806E6F6E6963}\0000

步骤 3:从注册表路径提取 Video GUID

if ($注册表路径 -match '\{[0-9A-Fa-f\-]+\}') {
    $视频GUID = $Matches[0]
    # 结果: {73430590-0080-11F0-8FFC-806E6F6E6963}
}

步骤 4:遍历所有显卡,查找视频标识匹配

$显卡列表 = Get-PnpDevice -Class Display -Status OK | Where-Object { 
    $_.FriendlyName -like '*NVIDIA*' -or $_.FriendlyName -like '*RTX*'
}

foreach ($显卡 in $显卡列表) {
    $设备参数路径 = "HKLM:\SYSTEM\CurrentControlSet\Enum\$($显卡.InstanceId)\Device Parameters"
    
    if (Test-Path $设备参数路径) {
        $参数 = Get-ItemProperty $设备参数路径
        
        if ($参数.VideoID -eq $视频GUID) {
            # 找到匹配!
            Write-Host "✓ Video$视频编号 → $($显卡.FriendlyName)"
            Write-Host "  实例标识: $($显卡.InstanceId)"
            return $显卡.InstanceId
        }
    }
}

实际示例

系统配置

  • 操作系统:Windows Server 2022
  • 显卡:4 × NVIDIA GeForce RTX 3090

映射结果

Video设备Video GUID物理显卡实例标识
Video2-5{7343058C...}PCI...\4&24FCC77D&0&0010
Video6-9{7343058E...}PCI...\4&CBB12F3&0&0010
Video10-13{73430590...}PCI...\4&EC904FF&0&0010
Video14-17{73430592...}PCI...\4&2B7D6FAD&0&0010

关键观察

  • 每块物理显卡对应 4 个 Video 设备(可能是 4 个显示输出接口)
  • 同一物理显卡的所有 Video 设备共享同一个视频标识GUID
  • 4 块显卡的视频标识GUID 完全不同,实现唯一标识

注册表结构

Video 设备注册表路径

HKLM:\HARDWARE\DEVICEMAP\VIDEO
  └─ \Device\Video10 = "\Registry\Machine\System\CurrentControlSet\Control\Video\{73430590-...}\0000"
  
HKLM:\System\CurrentControlSet\Control\Video\{73430590-0080-11F0-8FFC-806E6F6E6963}\0000
  ├─ DriverDesc = "NVIDIA GeForce RTX 3090"
  ├─ DXGKAdapterIndex = 10
  ├─ MatchingDeviceId = "pci\ven_10de&dev_2204"  (不精确,匹配所有同型号)
  └─ RMGpuId = 51712  (NVIDIA 内部唯一标识)

显卡 PnP 设备注册表路径

HKLM:\SYSTEM\CurrentControlSet\Enum\PCI\VEN_10DE&DEV_2204&...\4&EC904FF&0&0010
  ├─ FriendlyName = "NVIDIA GeForce RTX 3090"
  ├─ Driver = "{4d36e968-e325-11ce-bfc1-08002be10318}\0004"
  └─ Device Parameters\
      ├─ VideoID = {73430590-0080-11F0-8FFC-806E6F6E6963}  ← 关键!与 Video GUID 匹配
      └─ AOCID = {73430591-0080-11F0-8FFC-806E6F6E6963}

完整 PowerShell 函数

function Get-显卡实例标识从Video编号 {
    param([int]$Video编号)
    
    try {
        # 步骤1: 查询设备映射表
        $设备映射表 = Get-ItemProperty 'HKLM:\HARDWARE\DEVICEMAP\VIDEO' -ErrorAction SilentlyContinue
        if (-not $设备映射表) {
            Write-Warning "设备映射表不存在"
            return $null
        }
        
        $视频键名 = "\Device\Video$Video编号"
        $注册表路径 = $设备映射表.$视频键名
        
        if (-not $注册表路径) {
            Write-Warning "Video$Video编号 不在设备映射表中"
            return $null
        }
        
        # 步骤2: 提取 Video GUID
        if ($注册表路径 -notmatch '\{[0-9A-Fa-f\-]+\}') {
            Write-Warning "无法提取 GUID: $注册表路径"
            return $null
        }
        
        $视频GUID = $Matches[0]
        Write-Verbose "Video$Video编号 → GUID: $视频GUID"
        
        # 步骤3: 查找匹配的显卡
        $显卡列表 = Get-PnpDevice -Class Display -Status OK | Where-Object { 
            $_.FriendlyName -like '*NVIDIA*' -or $_.FriendlyName -like '*RTX*'
        }
        
        foreach ($显卡 in $显卡列表) {
            $设备参数路径 = "HKLM:\SYSTEM\CurrentControlSet\Enum\$($显卡.InstanceId)\Device Parameters"
            
            if (Test-Path $设备参数路径) {
                $参数 = Get-ItemProperty $设备参数路径 -ErrorAction SilentlyContinue
                
                if ($参数.VideoID -eq $视频GUID) {
                    Write-Host "✓ 精确匹配: Video$Video编号 → $($显卡.FriendlyName)" -ForegroundColor Green
                    return $显卡.InstanceId
                }
            }
        }
        
        Write-Warning "未找到视频标识匹配 $视频GUID 的显卡"
        return $null
        
    } catch {
        Write-Error "映射失败: $_"
        return $null
    }
}

# 使用示例
$实例标识 = Get-显卡实例标识从Video编号 -Video编号 10
if ($实例标识) {
    # 执行显卡重置
    Disable-PnpDevice -InstanceId $实例标识 -Confirm:$false
    Start-Sleep -Seconds 2
    Enable-PnpDevice -InstanceId $实例标识 -Confirm:$false
}

优势与限制

优势

100% 精确:视频标识GUID 是物理显卡的唯一标识
实时查询:不需要预建映射表,适应显卡配置变化
无需驱动API:纯注册表操作,无需 NVAPI 或 CUDA
跨驱动版本:所有现代 NVIDIA 驱动都使用这个机制

限制

⚠️ 依赖设备信息:FECS 错误必须包含 \Device\VideoXX 字段(约 50% 事件有此字段)
⚠️ 仅 NVIDIA 显卡:AMD 和 Intel 显卡可能使用不同的注册表结构


应用场景

  1. 自动化显卡故障恢复

    • 监听 nvlddmkm Event ID 13(FECS 错误)
    • 精确定位出错的显卡
    • 自动禁用/启用设备进行重置
  2. 多显卡系统诊断

    • 识别频繁出错的特定显卡
    • 生成每块显卡的错误统计报告
  3. 驱动开发与调试

    • 验证驱动安装后的设备映射
    • 测试多显卡配置的正确性

相关资源

Windows 注册表路径

  • 设备映射表HKLM:\HARDWARE\DEVICEMAP\VIDEO
  • Video 配置HKLM:\System\CurrentControlSet\Control\Video\{GUID}\0000
  • PnP 设备枚举HKLM:\SYSTEM\CurrentControlSet\Enum\PCI\...

PowerShell Cmdlets

  • Get-PnpDevice - 查询 PnP 设备
  • Get-ItemProperty - 读取注册表值
  • Get-WinEvent - 查询事件日志

事件日志

  • 日志名称:System
  • 提供程序:nvlddmkm
  • 事件标识:13(FECS Exception on Device)

附录:测试脚本

# 测试映射准确性
Write-Host "=== 显卡 → Video 设备映射测试 ===" -ForegroundColor Cyan

$显卡列表 = Get-PnpDevice -Class Display -Status OK | Where-Object {$_.FriendlyName -like '*3090*'}
Write-Host "检测到 $($显卡列表.Count) 块 RTX 3090`n"

foreach ($显卡 in $显卡列表) {
    $设备参数路径 = "HKLM:\SYSTEM\CurrentControlSet\Enum\$($显卡.InstanceId)\Device Parameters"
    if (Test-Path $设备参数路径) {
        $参数 = Get-ItemProperty $设备参数路径
        $视频GUID = $参数.VideoID
        
        Write-Host "显卡: $($显卡.InstanceId -replace '.*\\','')" -ForegroundColor Yellow
        Write-Host "  视频标识: $视频GUID"
        
        # 反向查找对应的 Video 设备
        $设备映射表 = Get-ItemProperty 'HKLM:\HARDWARE\DEVICEMAP\VIDEO'
        for ($i = 0; $i -le $设备映射表.MaxObjectNumber; $i++) {
            $注册表路径 = $设备映射表."\Device\Video$i"
            if ($注册表路径 -match $视频GUID) {
                Write-Host "  → Video$i" -ForegroundColor Green
            }
        }
        Write-Host ""
    }
}