【NVIDIA】NVIDIA Container Toolkit v1.19.1--07-NVCDI模块:CDI Spec生成引擎深度分析之七

07-NVCDI模块:CDI Spec生成引擎深度分析

NVIDIA Container Toolkit v1.19.1 源码深度分析

模块路径:pkg/nvcdi/

文档版本:1.0


目录

  • 一、模块定位
  • 二、模块整体结构
  • 三、核心逻辑深度解析
    • [3.1 api.go --- 接口定义与Feature Flag](#3.1 api.go — 接口定义与Feature Flag)
    • [3.2 lib.go --- 核心库与工厂函数](#3.2 lib.go — 核心库与工厂函数)
    • [3.3 options.go --- 配置选项系统](#3.3 options.go — 配置选项系统)
    • [3.4 mode.go --- 模式定义与自动检测](#3.4 mode.go — 模式定义与自动检测)
    • [3.5 wrapper.go --- 包装器与Spec生成](#3.5 wrapper.go — 包装器与Spec生成)
    • [3.6 namer.go --- 设备命名策略](#3.6 namer.go — 设备命名策略)
    • [3.7 lib-nvml.go --- NVML平台库](#3.7 lib-nvml.go — NVML平台库)
    • [3.8 lib-wsl.go --- WSL平台库](#3.8 lib-wsl.go — WSL平台库)
    • [3.9 lib-csv.go --- CSV平台库](#3.9 lib-csv.go — CSV平台库)
    • [3.10 management.go --- 管理模式库](#3.10 management.go — 管理模式库)
    • [3.11 gated.go --- 门控功能库](#3.11 gated.go — 门控功能库)
    • [3.12 lib-imex.go --- IMEX通道库](#3.12 lib-imex.go — IMEX通道库)
    • [3.13 spec/ 子目录分析](#3.13 spec/ 子目录分析)
    • [3.14 transform/ 子目录分析](#3.14 transform/ 子目录分析)
  • 四、七种平台库对比分析
  • 五、Mermaid图表汇总

一、模块定位

1.1 核心职责

NVCDI模块是NVIDIA Container Toolkit的CDI Spec生成引擎,负责:

  1. 发现宿主机上的NVIDIA GPU资源(设备节点、驱动库、固件等)
  2. 将资源信息转换为CDI(Container Device Interface)标准规范
  3. 生成可被容器运行时消费的CDI Spec文件

1.2 在系统中的位置

复制代码
容器运行时 (containerd / Docker / Podman)
    ↓ 请求CDI Spec
nvidia-ctk cdi generate 命令
    ↓ 调用
NVCDI模块 (pkg/nvcdi/)  ← 本文档
    ├── 调用 discover 模块 → 发现GPU资源
    ├── 调用 edits 模块   → 转换为CDI Edits
    ├── 调用 spec 模块    → 构建CDI Spec
    └── 调用 transform 模块 → 优化Spec
    ↓ 输出
CDI Spec文件 (.yaml / .json)
    ↓ 被容器运行时读取
容器获得GPU访问能力

1.3 支持的平台模式

模式 说明 典型场景
ModeNvml 使用NVML库发现GPU 标准Linux + NVIDIA GPU
ModeWsl WSL2专用模式 Windows Subsystem for Linux
ModeCSV 基于CSV文件发现设备 NVIDIA Tegra/Jetson
ModeManagement 管理模式(全设备) 容器管理工具
ModeGdrcopy GDR Copy GPU Direct RDMA Copy
ModeGds GDS GPU Direct Storage
ModeMofed MOFED Mellanox OFED
ModeNvswitch NVSwitch NVSwitch设备
ModeImex IMEX Channel IMEX通道设备
ModeAuto 自动检测 根据系统自动选择上述模式

二、模块整体结构

2.1 文件清单

文件 核心类型 职责
api.go Interface, SpecGenerator, DeviceSpecGenerator, FeatureFlag API接口定义
lib.go nvcdilib, New() 核心库与工厂函数
options.go options, Option, populateOptions() 配置选项
mode.go Mode, modes, resolveMode() 模式定义与自动检测
wrapper.go wrapper, deviceSpecGeneratorFactory 包装器
namer.go DeviceNamer, DeviceNamers, UUIDer 设备命名策略
lib-nvml.go nvmllib NVML平台实现
lib-wsl.go wsllib WSL平台实现
lib-csv.go csvlib, mixedcsvlib, csvDeviceGenerator CSV平台实现
management.go managementlib, managementDiscoverer 管理模式实现
gated.go gatedlib 门控功能实现
lib-imex.go imexlib, imexChannel IMEX通道实现
common-nvml.go (nvmllib方法) NVML公共发现器
full-gpu-nvml.go fullGPUDeviceSpecGenerator 全GPU Spec生成
mig-device-nvml.go migDeviceSpecGenerator MIG设备Spec生成
driver-nvml.go (nvcdilib方法) NVML驱动发现
driver-wsl.go nvidiaSMISimlinkHook WSL驱动发现
device-wsl.go (wsllib方法) WSL DXG设备发现
workarounds-device-folder-permissions.go deviceFolderPermissions 设备目录权限workaround
spec/api.go spec.Interface Spec接口定义
spec/builder.go builder, Option Spec构建器
spec/spec.go spec Spec实现
spec/set-minimum-version.go setMinimumRequiredVersion 版本检测Transform
transform/api.go Transformer Transform接口
transform/deduplicate.go dedupe 去重Transform
transform/simplify.go simplify 简化Transform
transform/merge.go merged 合并Transform
transform/merged-device.go mergedDevice 合并设备Transform
transform/sorter.go sorter 排序Transform
transform/remove.go remove 移除Transform
transform/edits.go containerEdits, deviceNode, mount, hook, env 编辑辅助类型
transform/root/ transformer, hostRootTransformer, containerRootTransformer Root路径Transform
transform/noop/ noop 空Transform

2.2 NVCDI模块类图

#mermaid-svg-hCQgGSWLFqdiwxfo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hCQgGSWLFqdiwxfo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hCQgGSWLFqdiwxfo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hCQgGSWLFqdiwxfo .error-icon{fill:#552222;}#mermaid-svg-hCQgGSWLFqdiwxfo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hCQgGSWLFqdiwxfo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hCQgGSWLFqdiwxfo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hCQgGSWLFqdiwxfo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hCQgGSWLFqdiwxfo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hCQgGSWLFqdiwxfo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hCQgGSWLFqdiwxfo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hCQgGSWLFqdiwxfo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hCQgGSWLFqdiwxfo .marker.cross{stroke:#333333;}#mermaid-svg-hCQgGSWLFqdiwxfo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hCQgGSWLFqdiwxfo p{margin:0;}#mermaid-svg-hCQgGSWLFqdiwxfo g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-hCQgGSWLFqdiwxfo g.classGroup text .title{font-weight:bolder;}#mermaid-svg-hCQgGSWLFqdiwxfo .cluster-label text{fill:#333;}#mermaid-svg-hCQgGSWLFqdiwxfo .cluster-label span{color:#333;}#mermaid-svg-hCQgGSWLFqdiwxfo .cluster-label span p{background-color:transparent;}#mermaid-svg-hCQgGSWLFqdiwxfo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hCQgGSWLFqdiwxfo .cluster text{fill:#333;}#mermaid-svg-hCQgGSWLFqdiwxfo .cluster span{color:#333;}#mermaid-svg-hCQgGSWLFqdiwxfo .nodeLabel,#mermaid-svg-hCQgGSWLFqdiwxfo .edgeLabel{color:#131300;}#mermaid-svg-hCQgGSWLFqdiwxfo .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-hCQgGSWLFqdiwxfo .label text{fill:#131300;}#mermaid-svg-hCQgGSWLFqdiwxfo .labelBkg{background:#ECECFF;}#mermaid-svg-hCQgGSWLFqdiwxfo .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-hCQgGSWLFqdiwxfo .classTitle{font-weight:bolder;}#mermaid-svg-hCQgGSWLFqdiwxfo .node rect,#mermaid-svg-hCQgGSWLFqdiwxfo .node circle,#mermaid-svg-hCQgGSWLFqdiwxfo .node ellipse,#mermaid-svg-hCQgGSWLFqdiwxfo .node polygon,#mermaid-svg-hCQgGSWLFqdiwxfo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hCQgGSWLFqdiwxfo .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo g.clickable{cursor:pointer;}#mermaid-svg-hCQgGSWLFqdiwxfo g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-hCQgGSWLFqdiwxfo g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-hCQgGSWLFqdiwxfo .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-hCQgGSWLFqdiwxfo .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-hCQgGSWLFqdiwxfo .dashed-line{stroke-dasharray:3;}#mermaid-svg-hCQgGSWLFqdiwxfo .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-hCQgGSWLFqdiwxfo #compositionStart,#mermaid-svg-hCQgGSWLFqdiwxfo .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo #compositionEnd,#mermaid-svg-hCQgGSWLFqdiwxfo .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo #dependencyStart,#mermaid-svg-hCQgGSWLFqdiwxfo .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo #dependencyStart,#mermaid-svg-hCQgGSWLFqdiwxfo .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo #extensionStart,#mermaid-svg-hCQgGSWLFqdiwxfo .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo #extensionEnd,#mermaid-svg-hCQgGSWLFqdiwxfo .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo #aggregationStart,#mermaid-svg-hCQgGSWLFqdiwxfo .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo #aggregationEnd,#mermaid-svg-hCQgGSWLFqdiwxfo .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo #lollipopStart,#mermaid-svg-hCQgGSWLFqdiwxfo .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo #lollipopEnd,#mermaid-svg-hCQgGSWLFqdiwxfo .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-hCQgGSWLFqdiwxfo .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-hCQgGSWLFqdiwxfo .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hCQgGSWLFqdiwxfo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hCQgGSWLFqdiwxfo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hCQgGSWLFqdiwxfo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 类型别名
类型别名
类型别名
类型别名
类型别名
持有
<<interface>>
Interface
+GetSpec(...string)(spec.Interface, error)
+GetCommonEdits()(*cdi.ContainerEdits, error)
+GetDeviceSpecsByID(...string)(\[\]specs.Device, error)
+GetAllDeviceSpecs()(\[\]specs.Device, error)
<<interface>>
SpecGenerator
+GetSpec(...string)(spec.Interface, error)
<<interface>>
DeviceSpecGenerator
+GetDeviceSpecs()(\[\]specs.Device, error)
<<interface>>
deviceSpecGeneratorFactory
+DeviceSpecGenerators(...string)(DeviceSpecGenerator, error)
+GetCommonEdits()(*cdi.ContainerEdits, error)
wrapper
-factory deviceSpecGeneratorFactory
-vendor string
-class string
-mergedDeviceOptions \[\]MergedDeviceOption
+GetSpec(...string)(spec.Interface, error)
+GetDeviceSpecsByID(...string)(\[\]specs.Device, error)
+GetAllDeviceSpecs()(\[\]specs.Device, error)
+GetCommonEdits()(*cdi.ContainerEdits, error)
nvcdilib
-logger logger.Interface
-platformlibs
-deviceNamers DeviceNamers
-devRoot string
-librarySearchPaths \[\]string
-driver *root.Driver
-featureFlags mapFeatureFlagbool
-hookCreator HookCreator
-editsFactory edits.Factory
nvmllib
+GetCommonEdits()(*cdi.ContainerEdits, error)
+DeviceSpecGenerators(...string)(DeviceSpecGenerator, error)
wsllib
+DeviceSpecGenerators(...string)(DeviceSpecGenerator, error)
+GetDeviceSpecs()(\[\]specs.Device, error)
+GetCommonEdits()(*cdi.ContainerEdits, error)
csvlib
+DeviceSpecGenerators(...string)(DeviceSpecGenerator, error)
+GetCommonEdits()(*cdi.ContainerEdits, error)
managementlib
+DeviceSpecGenerators(...string)(DeviceSpecGenerator, error)
+GetDeviceSpecs()(\[\]specs.Device, error)
+GetCommonEdits()(*cdi.ContainerEdits, error)
gatedlib
-mode Mode
+DeviceSpecGenerators(...string)(DeviceSpecGenerator, error)
+GetDeviceSpecs()(\[\]specs.Device, error)
+GetCommonEdits()(*cdi.ContainerEdits, error)
imexlib
+DeviceSpecGenerators(...string)(DeviceSpecGenerator, error)
+GetCommonEdits()(*cdi.ContainerEdits, error)
fullGPUDeviceSpecGenerator
migDeviceSpecGenerator
csvDeviceGenerator
imexChannel


三、核心逻辑深度解析

3.1 api.go --- 接口定义与Feature Flag

文件路径pkg/nvcdi/api.go

3.1.1 完整代码与逐行注释
go 复制代码
package nvcdi

import (
    "tags.cncf.io/container-device-interface/pkg/cdi"
    "tags.cncf.io/container-device-interface/specs-go"

    "github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
    "github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi/spec"
)

// Interface 定义nvcdi包的API
// 这是面向外部使用者的主要接口
type Interface interface {
    SpecGenerator           // 嵌入Spec生成能力
    GetCommonEdits() (*cdi.ContainerEdits, error)           // 获取公共编辑
    GetDeviceSpecsByID(...string) ([]specs.Device, error)   // 按ID获取设备Spec
    // Deprecated: 使用 GetDeviceSpecsByID("all") 替代
    GetAllDeviceSpecs() ([]specs.Device, error)
}

// SpecGenerator 定义生成完整CDI Spec的能力
type SpecGenerator interface {
    GetSpec(...string) (spec.Interface, error)
}

// DeviceSpecGenerator 定义生成设备Spec的能力
type DeviceSpecGenerator interface {
    GetDeviceSpecs() ([]specs.Device, error)
}

// HookName 是discover.HookName的类型别名
// 将discover模块的HookName暴露给nvcdi包
type HookName = discover.HookName

// Hook常量定义(从discover包重导出)
const (
    AllHooks = discover.AllHooks
    CreateSymlinksHook = discover.CreateSymlinksHook
    DisableDeviceNodeModificationHook = discover.DisableDeviceNodeModificationHook
    EnableCudaCompatHook = discover.EnableCudaCompatHook
    UpdateLDCacheHook = discover.UpdateLDCacheHook
    // 弃用别名
    HookCreateSymlinks = CreateSymlinksHook
    HookEnableCudaCompat = EnableCudaCompatHook
    HookUpdateLDCache = UpdateLDCacheHook
)

// FeatureFlag 表示可切换的特性标志
// 所有特性默认关闭
type FeatureFlag string

const (
    // 启用显式驱动库列表
    FeatureEnableExplicitDriverLibraries = FeatureFlag("enable-explicit-driver-libraries")
    // 禁用nvsandboxutils
    FeatureDisableNvsandboxUtils = FeatureFlag("disable-nvsandboxutils")
    // 启用coherent设备注解
    FeatureEnableCoherentAnnotations = FeatureFlag("enable-coherent-annotations")
    // 禁用CSV模式下的多设备处理
    FeatureDisableMultipleCSVDevices = FeatureFlag("disable-multiple-csv-devices")
    // 禁用设备节点的附加GID
    FeatureNoAdditionalGIDsForDeviceNodes = FeatureFlag("no-additional-gids-for-device-nodes")
    // 禁用IPC发现器
    FeatureDisableIPCDiscoverer = FeatureFlag("disable-ipc-discoverer")
)
3.1.2 接口层次分析

#mermaid-svg-aI7OOa8K37JwU2Kt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-aI7OOa8K37JwU2Kt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aI7OOa8K37JwU2Kt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aI7OOa8K37JwU2Kt .error-icon{fill:#552222;}#mermaid-svg-aI7OOa8K37JwU2Kt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aI7OOa8K37JwU2Kt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aI7OOa8K37JwU2Kt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aI7OOa8K37JwU2Kt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aI7OOa8K37JwU2Kt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aI7OOa8K37JwU2Kt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aI7OOa8K37JwU2Kt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aI7OOa8K37JwU2Kt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aI7OOa8K37JwU2Kt .marker.cross{stroke:#333333;}#mermaid-svg-aI7OOa8K37JwU2Kt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aI7OOa8K37JwU2Kt p{margin:0;}#mermaid-svg-aI7OOa8K37JwU2Kt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-aI7OOa8K37JwU2Kt .cluster-label text{fill:#333;}#mermaid-svg-aI7OOa8K37JwU2Kt .cluster-label span{color:#333;}#mermaid-svg-aI7OOa8K37JwU2Kt .cluster-label span p{background-color:transparent;}#mermaid-svg-aI7OOa8K37JwU2Kt .label text,#mermaid-svg-aI7OOa8K37JwU2Kt span{fill:#333;color:#333;}#mermaid-svg-aI7OOa8K37JwU2Kt .node rect,#mermaid-svg-aI7OOa8K37JwU2Kt .node circle,#mermaid-svg-aI7OOa8K37JwU2Kt .node ellipse,#mermaid-svg-aI7OOa8K37JwU2Kt .node polygon,#mermaid-svg-aI7OOa8K37JwU2Kt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aI7OOa8K37JwU2Kt .rough-node .label text,#mermaid-svg-aI7OOa8K37JwU2Kt .node .label text,#mermaid-svg-aI7OOa8K37JwU2Kt .image-shape .label,#mermaid-svg-aI7OOa8K37JwU2Kt .icon-shape .label{text-anchor:middle;}#mermaid-svg-aI7OOa8K37JwU2Kt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-aI7OOa8K37JwU2Kt .rough-node .label,#mermaid-svg-aI7OOa8K37JwU2Kt .node .label,#mermaid-svg-aI7OOa8K37JwU2Kt .image-shape .label,#mermaid-svg-aI7OOa8K37JwU2Kt .icon-shape .label{text-align:center;}#mermaid-svg-aI7OOa8K37JwU2Kt .node.clickable{cursor:pointer;}#mermaid-svg-aI7OOa8K37JwU2Kt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-aI7OOa8K37JwU2Kt .arrowheadPath{fill:#333333;}#mermaid-svg-aI7OOa8K37JwU2Kt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aI7OOa8K37JwU2Kt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aI7OOa8K37JwU2Kt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aI7OOa8K37JwU2Kt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-aI7OOa8K37JwU2Kt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aI7OOa8K37JwU2Kt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-aI7OOa8K37JwU2Kt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aI7OOa8K37JwU2Kt .cluster text{fill:#333;}#mermaid-svg-aI7OOa8K37JwU2Kt .cluster span{color:#333;}#mermaid-svg-aI7OOa8K37JwU2Kt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-aI7OOa8K37JwU2Kt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aI7OOa8K37JwU2Kt rect.text{fill:none;stroke-width:0;}#mermaid-svg-aI7OOa8K37JwU2Kt .icon-shape,#mermaid-svg-aI7OOa8K37JwU2Kt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aI7OOa8K37JwU2Kt .icon-shape p,#mermaid-svg-aI7OOa8K37JwU2Kt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-aI7OOa8K37JwU2Kt .icon-shape .label rect,#mermaid-svg-aI7OOa8K37JwU2Kt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aI7OOa8K37JwU2Kt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aI7OOa8K37JwU2Kt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aI7OOa8K37JwU2Kt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 实现
实现
实现
实现
实现
实现
实现
实现
实现
实现
实现
实现
实现
实现
实现
实现
实现
Interface
SpecGenerator

+GetSpec(...string)
+GetCommonEdits()
+GetDeviceSpecsByID(...string)
+GetAllDeviceSpecs() (deprecated)
wrapper
DeviceSpecGenerator

+GetDeviceSpecs()
fullGPUDeviceSpecGenerator
migDeviceSpecGenerator
csvDeviceGenerator
imexChannel
wsllib
managementlib
gatedlib
deviceSpecGeneratorFactory

+DeviceSpecGenerators(...string)

+GetCommonEdits()
nvmllib
csvlib
imexlib


3.2 lib.go --- 核心库与工厂函数

文件路径pkg/nvcdi/lib.go

3.2.1 nvcdilib结构体
go 复制代码
// nvcdilib 是NVCDI库的核心结构体
// 所有平台库都基于此结构体通过类型别名扩展
type nvcdilib struct {
    logger logger.Interface
    platformlibs          // 嵌入平台库依赖(nvml, device, info, nvsandboxutils)
    deviceNamers DeviceNamers   // 设备命名策略列表
    devRoot            string   // 设备根路径
    librarySearchPaths []string // 库搜索路径

    csv csvOptions             // CSV模式配置

    driver *root.Driver        // 驱动对象

    featureFlags map[FeatureFlag]bool  // 特性标志

    hookCreator  discover.HookCreator  // Hook创建器
    editsFactory edits.Factory          // Edits工厂
}
3.2.2 New()工厂函数完整注释
go 复制代码
// New 创建一个新的nvcdi库实例
// 这是面向外部使用者的入口函数
//
// 执行流程:
// 1. 解析配置选项 (populateOptions)
// 2. 构建nvcdilib基础结构
// 3. 根据mode选择deviceSpecGeneratorFactory
// 4. 创建wrapper包装factory
// 5. 返回Interface
func New(opts ...Option) (Interface, error) {
    // 1. 解析所有Option,填充options结构体
    o := populateOptions(opts...)

    // 2. 构建nvcdilib基础结构
    l := &nvcdilib{
        logger:       o.logger,
        platformlibs: o.platformlibs,
        driver: root.New(
            o.getDriverOptions()...,  // 配置Driver对象
        ),
        devRoot:      o.devRoot,
        deviceNamers: o.deviceNamers,

        librarySearchPaths: slices.Clone(o.librarySearchPaths),  // 深拷贝
        featureFlags:       o.featureFlags,

        csv: o.csv,

        // 创建Hook创建器
        hookCreator: discover.NewHookCreator(
            discover.WithNVIDIACDIHookPath(o.nvidiaCDIHookPath),
            discover.WithEnabledHooks(o.enabledHooks...),
            discover.WithLdconfigPath(o.ldconfigPath),
            discover.WithDisabledHooks(o.disabledHooks...),
        ),
        editsFactory: o.editsFactory,
    }

    // 3. 根据mode选择对应的factory
    var factory deviceSpecGeneratorFactory
    switch o.mode {
    case ModeCSV:
        factory = (*csvlib)(l)       // CSV模式
    case ModeManagement:
        factory = (*managementlib)(l) // 管理模式
    case ModeNvml:
        factory = (*nvmllib)(l)       // NVML模式
    case ModeWsl:
        factory = (*wsllib)(l)        // WSL模式
    case ModeGdrcopy, ModeGds, ModeMofed, ModeNvswitch:
        factory = &gatedlib{          // 门控模式
            nvcdilib: l,
            mode:     o.mode,
        }
    case ModeImex:
        factory = (*imexlib)(l)       // IMEX模式
    default:
        return nil, fmt.Errorf("unknown mode %q", o.mode)
    }

    // 4. 创建wrapper
    w := wrapper{
        factory:             factory,
        vendor:              o.getVendorOrDefault(),
        class:               o.getClassOrDefault(),
        mergedDeviceOptions: o.mergedDeviceOptions,
    }
    
    // 5. 返回wrapper作为Interface
    return &w, nil
}
3.2.3 版本器辅助类型
go 复制代码
// nvmllibAsVersioner 包装nvml.Interface,添加Version()方法
// 用于root.Driver的版本检测
type nvmllibAsVersioner struct {
    nvml.Interface
}

func nvmllibWithVersion(nvmllib nvml.Interface) *nvmllibAsVersioner {
    if nvmllib == nil {
        return nil
    }
    return &nvmllibAsVersioner{Interface: nvmllib}
}

// Version 通过NVML获取驱动版本
func (l *nvmllibAsVersioner) Version() (string, error) {
    if l == nil || l.Interface == nil {
        return "", fmt.Errorf("nvml library not initialized")
    }
    r := l.Init()
    if r != nvml.SUCCESS {
        return "", fmt.Errorf("failed to initialize nvml: %v", r)
    }
    defer func() { _ = l.Shutdown() }()
    version, r := l.SystemGetDriverVersion()
    if r != nvml.SUCCESS {
        return "", fmt.Errorf("failed to get driver version: %v", r)
    }
    return version, nil
}

// nvsandboxutilslibAsVersioner 类似,包装nvsandboxutils
type nvsandboxutilslibAsVersioner struct {
    nvsandboxutils.Interface
}

func nvsandboxutilslibWithVersion(nvsandboxutilslib nvsandboxutils.Interface) *nvsandboxutilslibAsVersioner {
    if nvsandboxutilslib == nil {
        return nil
    }
    return &nvsandboxutilslibAsVersioner{Interface: nvsandboxutilslib}
}

func (l *nvsandboxutilslibAsVersioner) Version() (string, error) {
    if l == nil || l.Interface == nil {
        return "", fmt.Errorf("libnvsandboxutils is not available")
    }
    version, ret := l.GetDriverVersion()
    if ret != nvsandboxutils.SUCCESS {
        return "", fmt.Errorf("%v", ret)
    }
    return version, nil
}

#mermaid-svg-oSGpnOtS7YhZQkZl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oSGpnOtS7YhZQkZl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oSGpnOtS7YhZQkZl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oSGpnOtS7YhZQkZl .error-icon{fill:#552222;}#mermaid-svg-oSGpnOtS7YhZQkZl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oSGpnOtS7YhZQkZl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oSGpnOtS7YhZQkZl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oSGpnOtS7YhZQkZl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oSGpnOtS7YhZQkZl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oSGpnOtS7YhZQkZl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oSGpnOtS7YhZQkZl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oSGpnOtS7YhZQkZl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oSGpnOtS7YhZQkZl .marker.cross{stroke:#333333;}#mermaid-svg-oSGpnOtS7YhZQkZl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oSGpnOtS7YhZQkZl p{margin:0;}#mermaid-svg-oSGpnOtS7YhZQkZl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oSGpnOtS7YhZQkZl .cluster-label text{fill:#333;}#mermaid-svg-oSGpnOtS7YhZQkZl .cluster-label span{color:#333;}#mermaid-svg-oSGpnOtS7YhZQkZl .cluster-label span p{background-color:transparent;}#mermaid-svg-oSGpnOtS7YhZQkZl .label text,#mermaid-svg-oSGpnOtS7YhZQkZl span{fill:#333;color:#333;}#mermaid-svg-oSGpnOtS7YhZQkZl .node rect,#mermaid-svg-oSGpnOtS7YhZQkZl .node circle,#mermaid-svg-oSGpnOtS7YhZQkZl .node ellipse,#mermaid-svg-oSGpnOtS7YhZQkZl .node polygon,#mermaid-svg-oSGpnOtS7YhZQkZl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oSGpnOtS7YhZQkZl .rough-node .label text,#mermaid-svg-oSGpnOtS7YhZQkZl .node .label text,#mermaid-svg-oSGpnOtS7YhZQkZl .image-shape .label,#mermaid-svg-oSGpnOtS7YhZQkZl .icon-shape .label{text-anchor:middle;}#mermaid-svg-oSGpnOtS7YhZQkZl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oSGpnOtS7YhZQkZl .rough-node .label,#mermaid-svg-oSGpnOtS7YhZQkZl .node .label,#mermaid-svg-oSGpnOtS7YhZQkZl .image-shape .label,#mermaid-svg-oSGpnOtS7YhZQkZl .icon-shape .label{text-align:center;}#mermaid-svg-oSGpnOtS7YhZQkZl .node.clickable{cursor:pointer;}#mermaid-svg-oSGpnOtS7YhZQkZl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oSGpnOtS7YhZQkZl .arrowheadPath{fill:#333333;}#mermaid-svg-oSGpnOtS7YhZQkZl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oSGpnOtS7YhZQkZl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oSGpnOtS7YhZQkZl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oSGpnOtS7YhZQkZl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oSGpnOtS7YhZQkZl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oSGpnOtS7YhZQkZl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oSGpnOtS7YhZQkZl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oSGpnOtS7YhZQkZl .cluster text{fill:#333;}#mermaid-svg-oSGpnOtS7YhZQkZl .cluster span{color:#333;}#mermaid-svg-oSGpnOtS7YhZQkZl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oSGpnOtS7YhZQkZl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oSGpnOtS7YhZQkZl rect.text{fill:none;stroke-width:0;}#mermaid-svg-oSGpnOtS7YhZQkZl .icon-shape,#mermaid-svg-oSGpnOtS7YhZQkZl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oSGpnOtS7YhZQkZl .icon-shape p,#mermaid-svg-oSGpnOtS7YhZQkZl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oSGpnOtS7YhZQkZl .icon-shape .label rect,#mermaid-svg-oSGpnOtS7YhZQkZl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oSGpnOtS7YhZQkZl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oSGpnOtS7YhZQkZl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oSGpnOtS7YhZQkZl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ModeNvml
ModeWsl
ModeCSV
ModeManagement
ModeGdrcopy/Gds/Mofed/Nvswitch
ModeImex
Unknown
New(opts...)
populateOptions(opts...)
构建nvcdilib
创建root.Driver
创建HookCreator
根据mode选择factory
(*nvmllib)(l)
(*wsllib)(l)
(*csvlib)(l)
(*managementlib)(l)
&gatedlib{nvcdilib:l, mode:o.mode}
(*imexlib)(l)
返回error
创建wrapper{factory, vendor, class, mergedDeviceOptions}
返回 &w as Interface


3.3 options.go --- 配置选项系统

文件路径pkg/nvcdi/options.go

3.3.1 options结构体
go 复制代码
type options struct {
    logger logger.Interface
    platformlibs          // 平台库依赖
    mode               Mode
    deviceNamers       DeviceNamers
    driverRoot         string
    devRoot            string
    nvidiaCDIHookPath  string
    ldconfigPath       string
    configSearchPaths  []string
    librarySearchPaths []string

    csv csvOptions         // CSV配置

    vendor string
    class  string

    mergedDeviceOptions []transform.MergedDeviceOption
    featureFlags map[FeatureFlag]bool

    disabledHooks []discover.HookName
    enabledHooks  []discover.HookName

    editsFactory edits.Factory
}

type platformlibs struct {
    nvmllib   nvml.Interface        // NVML库接口
    devicelib device.Interface      // nvlib设备接口
    infolib   info.Interface        // 信息接口
    nvsandboxutilslib nvsandboxutils.Interface  // nvsandboxutils接口
}
3.3.2 populateOptions()深度分析
go 复制代码
// populateOptions 应用所有功能选项并解析必需的默认值
func populateOptions(opts ...Option) *options {
    // 设置默认值
    o := &options{
        mode:              ModeAuto,           // 默认自动检测模式
        driverRoot:        "/",                // 默认驱动根路径
        nvidiaCDIHookPath: "/usr/bin/nvidia-cdi-hook",  // 默认Hook路径
    }
    // 应用所有Option
    for _, opt := range opts {
        opt(o)
    }
    
    // 设置日志默认值
    if o.logger == nil {
        o.logger = logger.New()
    }
    
    // 设置设备命名器默认值
    if len(o.deviceNamers) == 0 {
        indexNamer, _ := NewDeviceNamer(DeviceNameStrategyIndex)
        o.deviceNamers = []DeviceNamer{indexNamer}
    }
    
    // 设置devRoot默认值(跟随driverRoot)
    if o.devRoot == "" {
        o.devRoot = o.driverRoot
    }
    
    // 初始化nvsandboxutils库
    if o.nvsandboxutilslib == nil {
        o.nvsandboxutilslib = o.getNvsandboxUtilsLib()
    }
    
    // 初始化NVML库
    if o.nvmllib == nil {
        o.nvmllib = o.getNvmlLib()
    }
    
    // 初始化设备库
    if o.devicelib == nil {
        o.devicelib = device.New(o.nvmllib)
    }
    
    // 初始化信息库
    if o.infolib == nil {
        o.infolib = info.New(
            info.WithRoot(o.driverRoot),
            info.WithLogger(o.logger),
            info.WithNvmlLib(o.nvmllib),
            info.WithDeviceLib(o.devicelib),
        )
    }
    
    // 解析模式(如果是Auto,自动检测)
    o.mode = o.resolveMode()
    
    // CSV模式默认值
    if o.mode == ModeCSV && len(o.csv.Files) == 0 {
        o.csv.Files = csv.DefaultFileList()
    }
    if o.mode == ModeCSV && o.csv.CompatContainerRoot == "" {
        o.csv.CompatContainerRoot = defaultOrinCompatContainerRoot
    }
    
    // 管理模式禁用特定Hook
    if o.mode == ModeManagement {
        o.disabledHooks = append(o.disabledHooks, HookEnableCudaCompat, DisableDeviceNodeModificationHook)
    }
    
    // 创建Edits Factory
    if o.editsFactory == nil {
        o.editsFactory = edits.NewFactory(
            edits.WithLogger(o.logger),
            edits.WithNoAdditionalGIDsForDeviceNodes(o.featureFlags[FeatureNoAdditionalGIDsForDeviceNodes]),
        )
    }
    
    return o
}
3.3.3 Option函数清单
Option函数 参数 作用
WithDeviceLib(devicelib) device.Interface 设置设备库
WithInfoLib(infolib) info.Interface 设置信息库
WithDeviceNamers(namers...) ...DeviceNamer 设置设备命名策略
WithDriverRoot(root) string 设置驱动根路径
WithDevRoot(root) string 设置设备根路径
WithEditsFactory(factory) edits.Factory 设置Edits工厂
WithLogger(logger) logger.Interface 设置日志
WithNVIDIACDIHookPath(path) string 设置Hook可执行文件路径
WithNvmlLib(nvmllib) nvml.Interface 设置NVML库
WithMode(mode) 泛型 m modeConstraint 设置模式
WithVendor(vendor) string 设置CDI vendor
WithClass(class) string 设置CDI class
WithMergedDeviceOptions(opts...) ...MergedDeviceOption 设置合并设备选项
WithCSVFiles(files) []string 设置CSV文件列表
WithCSVIgnorePatterns(patterns) []string 设置CSV忽略模式
WithCSVCompatContainerRoot(root) string 设置兼容容器根
WithConfigSearchPaths(paths) []string 设置配置搜索路径
WithLibrarySearchPaths(paths) []string 设置库搜索路径
WithDisabledHooks(hooks...) 泛型 ...T 禁用Hook
WithEnabledHooks(hooks...) 泛型 ...T 启用Hook
WithFeatureFlags(flags...) 泛型 ...T 启用特性标志

3.4 mode.go --- 模式定义与自动检测

文件路径pkg/nvcdi/mode.go

3.4.1 模式常量
go 复制代码
type Mode string

const (
    ModeAuto       = Mode("auto")       // 自动检测
    ModeNvml       = Mode("nvml")       // NVML库
    ModeWsl        = Mode("wsl")        // WSL2
    ModeManagement = Mode("management") // 管理模式
    ModeGdrcopy    = Mode("gdrcopy")    // GDR Copy
    ModeGds        = Mode("gds")        // GDS
    ModeMofed      = Mode("mofed")      // MOFED
    ModeCSV        = Mode("csv")        // CSV文件
    ModeImex       = Mode("imex")       // IMEX通道
    ModeNvswitch   = Mode("nvswitch")   // NVSwitch
)
3.4.2 自动检测逻辑
go 复制代码
// resolveMode 在ModeAuto时自动检测平台
func (o *options) resolveMode() (rmode Mode) {
    if o.mode != ModeAuto {
        return o.mode  // 非Auto模式直接返回
    }
    defer func() {
        o.logger.Infof("Auto-detected mode as '%v'", rmode)
    }()

    platform := o.infolib.ResolvePlatform()
    switch platform {
    case info.PlatformNVML:
        return ModeNvml      // 标准NVIDIA GPU平台
    case info.PlatformTegra:
        return ModeCSV       // Tegra/Jetson平台
    case info.PlatformWSL:
        return ModeWsl       // WSL2平台
    }
    // 未知平台默认使用NVML
    o.logger.Warningf("Unsupported platform detected: %v; assuming %v", platform, ModeNvml)
    return ModeNvml
}

#mermaid-svg-HmWOKSVga7mbxW8T{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-HmWOKSVga7mbxW8T .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HmWOKSVga7mbxW8T .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HmWOKSVga7mbxW8T .error-icon{fill:#552222;}#mermaid-svg-HmWOKSVga7mbxW8T .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HmWOKSVga7mbxW8T .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HmWOKSVga7mbxW8T .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HmWOKSVga7mbxW8T .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HmWOKSVga7mbxW8T .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HmWOKSVga7mbxW8T .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HmWOKSVga7mbxW8T .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HmWOKSVga7mbxW8T .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HmWOKSVga7mbxW8T .marker.cross{stroke:#333333;}#mermaid-svg-HmWOKSVga7mbxW8T svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HmWOKSVga7mbxW8T p{margin:0;}#mermaid-svg-HmWOKSVga7mbxW8T .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HmWOKSVga7mbxW8T .cluster-label text{fill:#333;}#mermaid-svg-HmWOKSVga7mbxW8T .cluster-label span{color:#333;}#mermaid-svg-HmWOKSVga7mbxW8T .cluster-label span p{background-color:transparent;}#mermaid-svg-HmWOKSVga7mbxW8T .label text,#mermaid-svg-HmWOKSVga7mbxW8T span{fill:#333;color:#333;}#mermaid-svg-HmWOKSVga7mbxW8T .node rect,#mermaid-svg-HmWOKSVga7mbxW8T .node circle,#mermaid-svg-HmWOKSVga7mbxW8T .node ellipse,#mermaid-svg-HmWOKSVga7mbxW8T .node polygon,#mermaid-svg-HmWOKSVga7mbxW8T .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HmWOKSVga7mbxW8T .rough-node .label text,#mermaid-svg-HmWOKSVga7mbxW8T .node .label text,#mermaid-svg-HmWOKSVga7mbxW8T .image-shape .label,#mermaid-svg-HmWOKSVga7mbxW8T .icon-shape .label{text-anchor:middle;}#mermaid-svg-HmWOKSVga7mbxW8T .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HmWOKSVga7mbxW8T .rough-node .label,#mermaid-svg-HmWOKSVga7mbxW8T .node .label,#mermaid-svg-HmWOKSVga7mbxW8T .image-shape .label,#mermaid-svg-HmWOKSVga7mbxW8T .icon-shape .label{text-align:center;}#mermaid-svg-HmWOKSVga7mbxW8T .node.clickable{cursor:pointer;}#mermaid-svg-HmWOKSVga7mbxW8T .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HmWOKSVga7mbxW8T .arrowheadPath{fill:#333333;}#mermaid-svg-HmWOKSVga7mbxW8T .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HmWOKSVga7mbxW8T .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HmWOKSVga7mbxW8T .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HmWOKSVga7mbxW8T .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HmWOKSVga7mbxW8T .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HmWOKSVga7mbxW8T .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HmWOKSVga7mbxW8T .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HmWOKSVga7mbxW8T .cluster text{fill:#333;}#mermaid-svg-HmWOKSVga7mbxW8T .cluster span{color:#333;}#mermaid-svg-HmWOKSVga7mbxW8T div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-HmWOKSVga7mbxW8T .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HmWOKSVga7mbxW8T rect.text{fill:none;stroke-width:0;}#mermaid-svg-HmWOKSVga7mbxW8T .icon-shape,#mermaid-svg-HmWOKSVga7mbxW8T .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HmWOKSVga7mbxW8T .icon-shape p,#mermaid-svg-HmWOKSVga7mbxW8T .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HmWOKSVga7mbxW8T .icon-shape .label rect,#mermaid-svg-HmWOKSVga7mbxW8T .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HmWOKSVga7mbxW8T .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HmWOKSVga7mbxW8T .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HmWOKSVga7mbxW8T :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} No
Yes
PlatformNVML
PlatformTegra
PlatformWSL
Unknown
resolveMode()
mode == ModeAuto?
返回 mode
infolib.ResolvePlatform()
Platform类型
返回 ModeNvml
返回 ModeCSV
返回 ModeWsl
Warning + 返回 ModeNvml


3.5 wrapper.go --- 包装器与Spec生成

文件路径pkg/nvcdi/wrapper.go

3.5.1 完整代码与逐行注释
go 复制代码
// wrapper 包装deviceSpecGeneratorFactory,实现Interface接口
// 它是New()函数返回的实际类型
type wrapper struct {
    factory deviceSpecGeneratorFactory  // 平台库factory

    vendor string                       // CDI vendor(如"nvidia.com")
    class  string                       // CDI class(如"gpu")

    mergedDeviceOptions []transform.MergedDeviceOption  // 合并设备选项
}

// deviceSpecGeneratorFactory 定义平台库的工厂接口
type deviceSpecGeneratorFactory interface {
    // DeviceSpecGenerators 根据设备ID创建DeviceSpec生成器
    DeviceSpecGenerators(...string) (DeviceSpecGenerator, error)
    // GetCommonEdits 返回公共容器编辑(非设备特定的)
    GetCommonEdits() (*cdi.ContainerEdits, error)
}

// DeviceSpecGenerators 是DeviceSpecGenerator的组合列表
// 也实现DeviceSpecGenerator接口
type DeviceSpecGenerators []DeviceSpecGenerator

// GetSpec 生成完整的CDI Spec
// 这是Interface接口的核心方法
func (l *wrapper) GetSpec(devices ...string) (spec.Interface, error) {
    // 如果未指定设备,默认使用"all"
    if len(devices) == 0 {
        devices = append(devices, "all")
    }
    
    // 1. 获取设备Spec列表
    deviceSpecs, err := l.GetDeviceSpecsByID(devices...)
    if err != nil {
        return nil, err
    }
    
    // 2. 获取公共编辑
    edits, err := l.GetCommonEdits()
    if err != nil {
        return nil, err
    }
    
    // 3. 构建Spec
    return spec.New(
        spec.WithDeviceSpecs(deviceSpecs),         // 设备Spec列表
        spec.WithEdits(*edits.ContainerEdits),      // 公共编辑
        spec.WithVendor(l.vendor),                  // vendor
        spec.WithClass(l.class),                    // class
        spec.WithMergedDeviceOptions(l.mergedDeviceOptions...),  // 合并设备选项
    )
}

// GetDeviceSpecsByID 按ID获取设备Spec
func (l *wrapper) GetDeviceSpecsByID(devices ...string) ([]specs.Device, error) {
    // 通过factory创建DeviceSpecGenerator
    generators, err := l.factory.DeviceSpecGenerators(devices...)
    if err != nil {
        return nil, fmt.Errorf("failed to construct device spec generators: %w", err)
    }
    // 执行生成
    return generators.GetDeviceSpecs()
}

// GetAllDeviceSpecs 返回所有设备Spec(已弃用)
func (l *wrapper) GetAllDeviceSpecs() ([]specs.Device, error) {
    return l.GetDeviceSpecsByID("all")
}

// GetCommonEdits 返回公共编辑,并添加额外环境变量
func (m *wrapper) GetCommonEdits() (*cdi.ContainerEdits, error) {
    edits, err := m.factory.GetCommonEdits()
    if err != nil {
        return nil, err
    }
    // 添加 NVIDIA_VISIBLE_DEVICES=void 环境变量
    // 这确保CDI模式下不会通过NVIDIA_VISIBLE_DEVICES环境变量注入设备
    edits.Env = append(edits.Env, image.EnvVarNvidiaVisibleDevices+"=void")
    return edits, nil
}

// GetDeviceSpecs 合并所有DeviceSpecGenerator的结果
func (g DeviceSpecGenerators) GetDeviceSpecs() ([]specs.Device, error) {
    var allDeviceSpecs []specs.Device
    for _, dsg := range g {
        if dsg == nil {
            continue
        }
        deviceSpecs, err := dsg.GetDeviceSpecs()
        if err != nil {
            return nil, err
        }
        allDeviceSpecs = append(allDeviceSpecs, deviceSpecs...)
    }
    return allDeviceSpecs, nil
}

#mermaid-svg-wj0PsjI7uKh6hlMd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wj0PsjI7uKh6hlMd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wj0PsjI7uKh6hlMd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wj0PsjI7uKh6hlMd .error-icon{fill:#552222;}#mermaid-svg-wj0PsjI7uKh6hlMd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wj0PsjI7uKh6hlMd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wj0PsjI7uKh6hlMd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wj0PsjI7uKh6hlMd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wj0PsjI7uKh6hlMd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wj0PsjI7uKh6hlMd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wj0PsjI7uKh6hlMd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wj0PsjI7uKh6hlMd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wj0PsjI7uKh6hlMd .marker.cross{stroke:#333333;}#mermaid-svg-wj0PsjI7uKh6hlMd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wj0PsjI7uKh6hlMd p{margin:0;}#mermaid-svg-wj0PsjI7uKh6hlMd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wj0PsjI7uKh6hlMd .cluster-label text{fill:#333;}#mermaid-svg-wj0PsjI7uKh6hlMd .cluster-label span{color:#333;}#mermaid-svg-wj0PsjI7uKh6hlMd .cluster-label span p{background-color:transparent;}#mermaid-svg-wj0PsjI7uKh6hlMd .label text,#mermaid-svg-wj0PsjI7uKh6hlMd span{fill:#333;color:#333;}#mermaid-svg-wj0PsjI7uKh6hlMd .node rect,#mermaid-svg-wj0PsjI7uKh6hlMd .node circle,#mermaid-svg-wj0PsjI7uKh6hlMd .node ellipse,#mermaid-svg-wj0PsjI7uKh6hlMd .node polygon,#mermaid-svg-wj0PsjI7uKh6hlMd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wj0PsjI7uKh6hlMd .rough-node .label text,#mermaid-svg-wj0PsjI7uKh6hlMd .node .label text,#mermaid-svg-wj0PsjI7uKh6hlMd .image-shape .label,#mermaid-svg-wj0PsjI7uKh6hlMd .icon-shape .label{text-anchor:middle;}#mermaid-svg-wj0PsjI7uKh6hlMd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wj0PsjI7uKh6hlMd .rough-node .label,#mermaid-svg-wj0PsjI7uKh6hlMd .node .label,#mermaid-svg-wj0PsjI7uKh6hlMd .image-shape .label,#mermaid-svg-wj0PsjI7uKh6hlMd .icon-shape .label{text-align:center;}#mermaid-svg-wj0PsjI7uKh6hlMd .node.clickable{cursor:pointer;}#mermaid-svg-wj0PsjI7uKh6hlMd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wj0PsjI7uKh6hlMd .arrowheadPath{fill:#333333;}#mermaid-svg-wj0PsjI7uKh6hlMd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wj0PsjI7uKh6hlMd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wj0PsjI7uKh6hlMd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wj0PsjI7uKh6hlMd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wj0PsjI7uKh6hlMd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wj0PsjI7uKh6hlMd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wj0PsjI7uKh6hlMd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wj0PsjI7uKh6hlMd .cluster text{fill:#333;}#mermaid-svg-wj0PsjI7uKh6hlMd .cluster span{color:#333;}#mermaid-svg-wj0PsjI7uKh6hlMd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wj0PsjI7uKh6hlMd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wj0PsjI7uKh6hlMd rect.text{fill:none;stroke-width:0;}#mermaid-svg-wj0PsjI7uKh6hlMd .icon-shape,#mermaid-svg-wj0PsjI7uKh6hlMd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wj0PsjI7uKh6hlMd .icon-shape p,#mermaid-svg-wj0PsjI7uKh6hlMd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wj0PsjI7uKh6hlMd .icon-shape .label rect,#mermaid-svg-wj0PsjI7uKh6hlMd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wj0PsjI7uKh6hlMd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wj0PsjI7uKh6hlMd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wj0PsjI7uKh6hlMd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
wrapper.GetSpec(devices...)
devices为空?
devices = 'all'
使用传入的devices
GetDeviceSpecsByID(devices...)
factory.DeviceSpecGenerators(devices...)
generators.GetDeviceSpecs()
deviceSpecs \[\]specs.Device
factory.GetCommonEdits()
edits *cdi.ContainerEdits
添加 NVIDIA_VISIBLE_DEVICES=void
spec.New(...)
spec.WithDeviceSpecs
spec.WithEdits
spec.WithVendor
spec.WithClass
spec.WithMergedDeviceOptions
返回 spec.Interface


3.6 namer.go --- 设备命名策略

文件路径pkg/nvcdi/namer.go

go 复制代码
// UUIDer 是获取UUID的接口
type UUIDer interface {
    GetUUID() (string, error)
}

// DeviceNamers 是DeviceNamer的列表
type DeviceNamers []DeviceNamer

// DeviceNamer 定义设备命名接口
type DeviceNamer interface {
    GetDeviceName(int, UUIDer) (string, error)
    GetMigDeviceName(int, UUIDer, int, UUIDer) (string, error)
}

// 三种命名策略
const (
    DeviceNameStrategyIndex    = "index"      // 如 "0", "1:0"
    DeviceNameStrategyTypeIndex = "type-index" // 如 "gpu0", "mig1:0"
    DeviceNameStrategyUUID     = "uuid"        // 如 "GPU-xxxx-xxxx"
)

// deviceNameIndex 基于索引命名
type deviceNameIndex struct {
    gpuPrefix string  // "" for "index", "gpu" for "type-index"
    migPrefix string  // "" for "index", "mig" for "type-index"
}

func (s deviceNameIndex) GetDeviceName(i int, _ UUIDer) (string, error) {
    return fmt.Sprintf("%s%d", s.gpuPrefix, i), nil
}

func (s deviceNameIndex) GetMigDeviceName(i int, _ UUIDer, j int, _ UUIDer) (string, error) {
    return fmt.Sprintf("%s%d:%d", s.migPrefix, i, j), nil
}

// deviceNameUUID 基于UUID命名
type deviceNameUUID struct{}

func (s deviceNameUUID) GetDeviceName(i int, d UUIDer) (string, error) {
    uuid, err := d.GetUUID()
    if err != nil {
        return "", fmt.Errorf("failed to get device UUID: %v", err)
    }
    return uuid, nil
}

func (s deviceNameUUID) GetMigDeviceName(i int, _ UUIDer, j int, mig UUIDer) (string, error) {
    uuid, err := mig.GetUUID()
    if err != nil {
        return "", fmt.Errorf("failed to get device UUID: %v", err)
    }
    return uuid, nil
}

3.7 lib-nvml.go --- NVML平台库

文件路径pkg/nvcdi/lib-nvml.go

nvmllib 是最核心的平台库,处理标准Linux + NVIDIA GPU场景。

3.7.1 核心方法
go 复制代码
type nvmllib nvcdilib  // 类型别名

// GetCommonEdits 生成适用于所有设备的公共编辑
// 包括:控制设备节点、图形挂载、驱动库
func (l *nvmllib) GetCommonEdits() (*cdi.ContainerEdits, error) {
    common, err := l.newCommonNVMLDiscoverer()
    if err != nil {
        return nil, fmt.Errorf("failed to create discoverer for common entities: %v", err)
    }
    return l.editsFactory.FromDiscoverer(common)
}

// DeviceSpecGenerators 根据ID创建设备Spec生成器
// 支持的ID:GPU索引、UUID、"all"、"none"
func (l *nvmllib) DeviceSpecGenerators(ids ...string) (DeviceSpecGenerator, error) {
    if err := l.init(); err != nil {
        return nil, err
    }
    defer l.tryShutdown()
    dsgs, err := l.getDeviceSpecGeneratorsForIDs(ids...)
    if err != nil {
        return nil, err
    }
    return l.withInit(dsgs), nil  // 包装init/shutdown
}

// getDeviceSpecGeneratorsForIDs 根据ID列表创建生成器
func (l *nvmllib) getDeviceSpecGeneratorsForIDs(ids ...string) (DeviceSpecGenerator, error) {
    var identifiers []device.Identifier
    for _, id := range ids {
        if id == "none" {
            return emptyDeviceSpecGenerator("none"), nil  // 空设备
        }
        if id == "all" {
            return l.getDeviceSpecGeneratorsForAllDevices()  // 所有设备
        }
        identifiers = append(identifiers, device.Identifier(id))
    }
    
    // 规范化设备ID(索引→UUID)
    uuids, err := l.normalizeDeviceIDs(identifiers...)
    if err != nil {
        return nil, err
    }
    
    // 为每个UUID创建生成器
    var DeviceSpecGenerators DeviceSpecGenerators
    for _, uuid := range uuids {
        device, ret := l.nvmllib.DeviceGetHandleByUUID(string(uuid))
        if ret != nvml.SUCCESS {
            return nil, fmt.Errorf("failed to get device handle from UUID: %v", ret)
        }
        generator, err := l.newDeviceSpecGeneratorFromNVMLDevice(string(uuid), device)
        if err != nil {
            return nil, err
        }
        DeviceSpecGenerators = append(DeviceSpecGenerators, generator)
    }
    return DeviceSpecGenerators, nil
}

// newDeviceSpecGeneratorFromNVMLDevice 判断是Full GPU还是MIG设备
func (l *nvmllib) newDeviceSpecGeneratorFromNVMLDevice(id string, nvmlDevice nvml.Device) (DeviceSpecGenerator, error) {
    isMig, ret := nvmlDevice.IsMigDeviceHandle()
    if ret != nvml.SUCCESS {
        return nil, fmt.Errorf("%v", ret)
    }
    if isMig {
        return l.newMIGDeviceSpecGeneratorFromNVMLDevice(id, nvmlDevice)
    }
    return l.newFullGPUDeviceSpecGeneratorFromNVMLDevice(id, nvmlDevice, l.featureFlags)
}

3.8 lib-wsl.go --- WSL平台库

go 复制代码
type wsllib nvcdilib

// GetDeviceSpecs 返回单个"all"设备的Spec
func (l *wsllib) GetDeviceSpecs() ([]specs.Device, error) {
    device := l.newDXGDeviceDiscoverer()
    deviceEdits, err := l.editsFactory.FromDiscoverer(device)
    if err != nil {
        return nil, fmt.Errorf("failed to create container edits for DXG device: %v", err)
    }
    deviceSpec := specs.Device{
        Name:           "all",
        ContainerEdits: *deviceEdits.ContainerEdits,
    }
    return []specs.Device{deviceSpec}, nil
}

// GetCommonEdits 返回WSL驱动的公共编辑
func (l *wsllib) GetCommonEdits() (*cdi.ContainerEdits, error) {
    driver, err := l.newWSLDriverDiscoverer()
    if err != nil {
        return nil, fmt.Errorf("failed to create discoverer for WSL driver: %v", err)
    }
    return l.editsFactory.FromDiscoverer(driver)
}

3.9 lib-csv.go --- CSV平台库

CSV平台库是最复杂的平台实现,支持纯CSV模式和混合模式(CSV+NVML)。

3.9.1 核心逻辑
go 复制代码
type csvlib nvcdilib
type mixedcsvlib nvcdilib

// DeviceSpecGenerators 决定使用纯CSV还是混合模式
func (l *csvlib) DeviceSpecGenerators(ids ...string) (DeviceSpecGenerator, error) {
    if l.usePureCSVDeviceSpecGenerator() {
        return l.purecsvDeviceSpecGenerators(ids...)
    }
    // 混合模式失败时降级为纯CSV
    mixed, err := l.mixedDeviceSpecGenerators(ids...)
    if err != nil {
        l.logger.Warningf("Falling back to pure CSV: %v", err)
        return l.purecsvDeviceSpecGenerators(ids...)
    }
    return mixed, nil
}

// usePureCSVDeviceSpecGenerator 判断是否使用纯CSV模式
func (l *csvlib) usePureCSVDeviceSpecGenerator() bool {
    if l.featureFlags[FeatureDisableMultipleCSVDevices] { return true }
    hasNVML, _ := l.infolib.HasNvml()
    if !hasNVML { return true }
    // 初始化NVML失败也使用纯CSV
    asNvmlLib := (*nvmllib)(l)
    if err := asNvmlLib.init(); err != nil { return true }
    defer asNvmlLib.tryShutdown()
    numDevices, ret := l.nvmllib.DeviceGetCount()
    if ret != nvml.SUCCESS { return true }
    return numDevices <= 1  // 单设备也使用纯CSV
}
3.9.2 iGPU/dGPU区分

混合CSV模式区分集成GPU(iGPU)和离散GPU(dGPU):

go 复制代码
func (l *mixedcsvlib) csvDeviceSpecGenerator(index int, uuid string, device nvml.Device) (DeviceSpecGenerator, error) {
    isIntegrated, err := isIntegratedGPU(device)
    if err != nil {
        return nil, fmt.Errorf("is-integrated check failed")
    }
    if isIntegrated {
        return l.iGPUDeviceSpecGenerator(index, uuid)
    }
    return l.dGPUDeviceSpecGenerator(index, uuid)
}

// isIntegratedGPU 检测是否为集成GPU
// 启发式方法:
// 1. 不支持PCI信息 → 检查设备名(Orin/Thor)
// 2. 支持PCI信息 → 检查特定Bus ID(Thor: 0000:01:00.0)
func isIntegratedGPU(d nvml.Device) (bool, error) {
    pciInfo, ret := d.GetPciInfo()
    if ret == nvml.ERROR_NOT_SUPPORTED {
        name, ret := d.GetName()
        if ret != nvml.SUCCESS { return false, fmt.Errorf("failed to get device name: %v", ret) }
        return info.IsIntegratedGPUName(name), nil
    }
    if ret != nvml.SUCCESS { return false, fmt.Errorf("failed to get PCI info: %v", ret) }
    if pciInfo.Domain == 0 && pciInfo.Bus == 1 && pciInfo.Device == 0 {
        return true, nil  // Thor iGPU的固定Bus ID
    }
    return false, nil
}

3.10 management.go --- 管理模式库

go 复制代码
type managementlib nvcdilib

// GetDeviceSpecs 返回单个"all"设备,包含所有NVIDIA设备节点
func (l *managementlib) GetDeviceSpecs() ([]specs.Device, error) {
    devices, err := l.newManagementDeviceDiscoverer()
    if err != nil { return nil, err }
    edits, err := l.editsFactory.FromDiscoverer(devices)
    if err != nil { return nil, err }
    if len(edits.DeviceNodes) == 0 {
        return nil, fmt.Errorf("no NVIDIA device nodes found")
    }
    device := specs.Device{
        Name:           "all",
        ContainerEdits: *edits.ContainerEdits,
    }
    return []specs.Device{device}, nil
}

// 管理模式过滤特定前缀的设备节点
func (m managementDiscoverer) nodeIsBlocked(path string) bool {
    blockedPrefixes := []string{"nvidia-fs", "nvidia-nvswitch", "nvidia-nvlink"}
    nodeName := filepath.Base(path)
    for _, prefix := range blockedPrefixes {
        if strings.HasPrefix(nodeName, prefix) { return true }
    }
    return false
}

3.11 gated.go --- 门控功能库

go 复制代码
type gatedlib struct {
    *nvcdilib
    mode Mode
}

// 根据mode选择不同的Discoverer
func (l *gatedlib) getModeDiscoverer() (discover.Discover, error) {
    switch l.mode {
    case ModeGdrcopy:
        return discover.NewGDRCopyDiscoverer(l.logger, l.driver)
    case ModeGds:
        return discover.NewGDSDiscoverer(l.logger, l.driver)
    case ModeMofed:
        return discover.NewMOFEDDiscoverer(l.logger, l.driver)
    case ModeNvswitch:
        return discover.NewNvSwitchDiscoverer(l.logger, l.driver)
    default:
        return nil, fmt.Errorf("unrecognized mode")
    }
}

// 门控功能的公共编辑为空
func (l *gatedlib) GetCommonEdits() (*cdi.ContainerEdits, error) {
    return l.editsFactory.FromDiscoverer(discover.None{})
}

3.12 lib-imex.go --- IMEX通道库

go 复制代码
type imexlib nvcdilib
type imexChannel struct {
    id      string
    devRoot string
}

const classImexChannel = "imex-channel"

// IMEX通道公共编辑为空
func (l *imexlib) GetCommonEdits() (*cdi.ContainerEdits, error) {
    return l.editsFactory.FromDiscoverer(discover.None{})
}

// DeviceSpecGenerators 根据通道ID创建生成器
// 支持的ID格式:数字、"channel数字"、"all"
func (l *imexlib) DeviceSpecGenerators(ids ...string) (DeviceSpecGenerator, error) {
    channelsIDs, err := l.getChannelIDs(ids...)
    if err != nil { return nil, err }
    var deviceSpecGenerators DeviceSpecGenerators
    for _, id := range channelsIDs {
        deviceSpecGenerators = append(deviceSpecGenerators, &imexChannel{id: id, devRoot: l.driver.DevRoot})
    }
    return deviceSpecGenerators, nil
}

// GetDeviceSpecs 为单个IMEX通道生成Spec
func (l *imexChannel) GetDeviceSpecs() ([]specs.Device, error) {
    path := "/dev/nvidia-caps-imex-channels/channel" + l.id
    deviceSpec := specs.Device{
        Name: l.id,
        ContainerEdits: specs.ContainerEdits{
            DeviceNodes: []*specs.DeviceNode{
                {Path: path, HostPath: filepath.Join(l.devRoot, path)},
            },
        },
    }
    return []specs.Device{deviceSpec}, nil
}

3.13 spec/ 子目录分析

3.13.1 spec.Interface
go 复制代码
type Interface interface {
    io.WriterTo
    Save(string) error
    Raw() *specs.Spec
}
3.13.2 spec.builder
go 复制代码
type builder struct {
    raw         *cdi.Spec           // 原始Spec(可选)
    version     string              // CDI版本
    vendor      string              // vendor
    class       string              // class
    deviceSpecs []cdi.Device        // 设备Spec列表
    edits       cdi.ContainerEdits  // 公共编辑
    format      string              // 输出格式
    mergedDeviceOptions []transform.MergedDeviceOption
    noSimplify          bool
    permissions         os.FileMode
    transformOnSave     transform.Transformer
}

// Build 构建最终的spec
func (o *builder) Build() (*spec, error) {
    raw := o.raw
    if raw == nil {
        raw = &cdi.Spec{
            Version:        o.version,
            Kind:           fmt.Sprintf("%s/%s", o.vendor, o.class),
            Devices:        o.deviceSpecs,
            ContainerEdits: o.edits,
        }
    }
    // 简化(除非禁用)
    if !o.noSimplify {
        err := transform.NewSimplifier().Transform(raw)
        if err != nil { return nil, err }
    }
    // 合并设备(如果配置了)
    if len(o.mergedDeviceOptions) > 0 {
        merge, err := transform.NewMergedDevice(o.mergedDeviceOptions...)
        if err != nil { return nil, err }
        if err := merge.Transform(raw); err != nil { return nil, err }
    }
    return &spec{Spec: raw, format: o.format, permissions: o.permissions, transformOnSave: o.transformOnSave}, nil
}
3.13.3 spec.spec.Save
go 复制代码
func (s *spec) Save(path string) error {
    // 1. 应用保存前Transform(如设置最小版本)
    if s.transformOnSave != nil {
        err := s.transformOnSave.Transform(s.Raw())
        if err != nil { return err }
    }
    // 2. 规范化路径
    path, err := s.normalizePath(path)
    // 3. 使用CDI Cache写入Spec文件
    cache, _ := cdi.NewCache(cdi.WithAutoRefresh(false), cdi.WithSpecDirs(specDir))
    if err := cache.WriteSpec(s.Raw(), filename); err != nil { return err }
    // 4. 设置文件权限
    specDirAsRoot, err := os.OpenRoot(specDir)
    if err := specDirAsRoot.Chmod(filename, s.permissions); err != nil { return err }
    return nil
}
3.13.4 setMinimumRequiredVersion
go 复制代码
type setMinimumRequiredVersion struct{}

func (d setMinimumRequiredVersion) Transform(spec *specs.Spec) error {
    minVersion, err := cdi.MinimumRequiredVersion(spec)
    if err != nil { return err }
    spec.Version = minVersion
    return nil
}

3.14 transform/ 子目录分析

3.14.1 Transformer接口
go 复制代码
type Transformer interface {
    Transform(*specs.Spec) error
}
3.14.2 deduplicate.go --- 去重
go 复制代码
type dedupe struct{}

// Transform 去重Spec中的重复实体
func (d dedupe) Transform(spec *specs.Spec) error {
    if spec == nil { return nil }
    // 去重公共编辑
    if err := d.transformEdits(&spec.ContainerEdits); err != nil { return err }
    // 去重每个设备的编辑
    for _, device := range spec.Devices {
        if err := d.transformEdits(&device.ContainerEdits); err != nil { return err }
    }
    return nil
}

// 每种实体类型的去重方法
func (d dedupe) deduplicateDeviceNodes(entities []*specs.DeviceNode) ([]*specs.DeviceNode, error) {
    // 使用JSON序列化作为唯一ID
    seen := make(map[string]bool)
    for _, e := range entities {
        id, err := deviceNode(*e).id()  // JSON marshal作为ID
        if seen[id] { continue }
        seen[id] = true
        // ...
    }
}
3.14.3 simplify.go --- 简化
go 复制代码
type simplify struct{}

// NewSimplifier 创建简化Transformer
// 组合了三个Transformer:dedupe → simplify → sorter
func NewSimplifier() Transformer {
    return Merge(dedupe{}, simplify{}, sorter{})
}

// Transform 从设备编辑中移除与公共编辑重复的实体
func (s simplify) Transform(spec *specs.Spec) error {
    // 1. 先去重
    dedupe := dedupe{}
    dedupe.Transform(spec)
    // 2. 获取公共编辑的实体ID集合
    commonEntityIDs, _ := (*containerEdits)(&spec.ContainerEdits).getEntityIds()
    // 3. 从每个设备编辑中移除公共实体
    toRemove := newRemover(commonEntityIDs...)
    for _, device := range spec.Devices {
        deviceAsSpec := specs.Spec{ContainerEdits: device.ContainerEdits}
        toRemove.Transform(&deviceAsSpec)
        // 4. 仅当结果非空时更新(避免空编辑的无效设备)
        if !(containerEdits)(deviceAsSpec.ContainerEdits).IsEmpty() {
            device.ContainerEdits = deviceAsSpec.ContainerEdits
        }
    }
    return nil
}
3.14.4 merge.go --- 合并
go 复制代码
type merged []Transformer

func Merge(transformers ...Transformer) Transformer {
    return merged(transformers)
}

// Transform 依次应用所有Transformer
func (t merged) Transform(spec *specs.Spec) error {
    for _, transformer := range t {
        if err := transformer.Transform(spec); err != nil {
            return err
        }
    }
    return nil
}
3.14.5 merged-device.go --- 合并设备
go 复制代码
type mergedDevice struct {
    name         string  // 默认"all"
    skipIfExists bool
    simplifier   Transformer
}

// Transform 添加一个合并设备到Spec
func (m mergedDevice) Transform(spec *specs.Spec) error {
    // 1. 检查是否已存在同名设备
    mergedDevice, err := mergeDeviceSpecs(spec.Devices, m.name)
    if mergedDevice == nil {
        if m.skipIfExists { return nil }
        return fmt.Errorf("device %q already exists", m.name)
    }
    // 2. 追加合并设备
    spec.Devices = append(spec.Devices, *mergedDevice)
    // 3. 简化Spec
    m.simplifier.Transform(spec)
    return nil
}

// mergeDeviceSpecs 合并所有设备的编辑为一个设备
func mergeDeviceSpecs(deviceSpecs []specs.Device, mergedDeviceName string) (*specs.Device, error) {
    for _, d := range deviceSpecs {
        if d.Name == mergedDeviceName { return nil, nil }  // 已存在
    }
    mergedEdits := edits.EmptyFactory.New()
    for _, d := range deviceSpecs {
        edit := cdi.ContainerEdits{ContainerEdits: &d.ContainerEdits}
        mergedEdits.Append(&edit)
    }
    return &specs.Device{Name: mergedDeviceName, ContainerEdits: *mergedEdits.ContainerEdits}, nil
}
3.14.6 sorter.go --- 排序
go 复制代码
type sorter struct{}

// Transform 对Spec中的实体进行排序
func (d sorter) Transform(spec *specs.Spec) error {
    // 排序公共编辑
    d.transformEdits(&spec.ContainerEdits)
    // 排序每个设备的编辑
    for _, device := range spec.Devices {
        d.transformEdits(&device.ContainerEdits)
    }
    // 按名称排序设备
    spec.Devices = d.sortDevices(spec.Devices)
    return nil
}

// 设备节点按路径深度排序(浅路径在前),相同深度按路径名排序
func (d sorter) sortDeviceNodes(entities []*specs.DeviceNode) []*specs.DeviceNode {
    sort.Slice(entities, func(i, j int) bool {
        ip := strings.Count(filepath.Clean(entities[i].Path), string(os.PathSeparator))
        jp := strings.Count(filepath.Clean(entities[j].Path), string(os.PathSeparator))
        if ip == jp { return entities[i].Path < entities[j].Path }
        return ip < jp
    })
    return entities
}
3.14.7 remove.go --- 移除
go 复制代码
type remove map[string]bool

// Transform 从Spec中移除指定ID的实体
func (r remove) Transform(spec *specs.Spec) error {
    // 从每个设备编辑中移除
    for _, device := range spec.Devices {
        r.transformEdits(&device.ContainerEdits)
    }
    // 从公共编辑中移除
    return r.transformEdits(&spec.ContainerEdits)
}
3.14.8 edits.go --- 实体ID生成
go 复制代码
// 每种实体类型通过JSON序列化生成唯一ID
type deviceNode specs.DeviceNode
func (dn deviceNode) id() (string, error) {
    b, err := json.Marshal(dn)
    return string(b), err
}

type mount specs.Mount
func (m mount) id() (string, error) {
    b, err := json.Marshal(m)
    return string(b), err
}

type hook specs.Hook
func (m hook) id() (string, error) {
    b, err := json.Marshal(m)
    return string(b), err
}

type env string
func (e env) id() (string, error) {
    return string(e), nil  // 环境变量本身就是唯一ID
}
3.14.9 root/ --- 路径Transform
go 复制代码
// root.transformer 转换路径的根
type transformer struct {
    root       string  // 原始根
    targetRoot string  // 目标根
}

func (t transformer) transformPath(path string) string {
    if !strings.HasPrefix(path, t.root) { return path }
    return filepath.Join(t.targetRoot, strings.TrimPrefix(path, t.root))
}

// hostRootTransformer 转换Host路径
// 影响DeviceNode.HostPath, Mount.HostPath, Hook.Path(非startContainer)
type hostRootTransformer transformer

// containerRootTransformer 转换容器路径
// 影响DeviceNode.Path, Mount.ContainerPath, Hook.Path(仅startContainer)
type containerRootTransformer transformer

// New根据配置选择transformer
func (b *builder) build() transform.Transformer {
    if b.root == b.targetRoot { return noop.New() }  // 相同则no-op
    if b.relativeTo == "container" { return containerRootTransformer(b.transformer) }
    return hostRootTransformer(b.transformer)
}

四、七种平台库对比分析

4.1 七种平台库策略分发图

#mermaid-svg-mEbdZBrko0HvkOMb{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mEbdZBrko0HvkOMb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mEbdZBrko0HvkOMb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mEbdZBrko0HvkOMb .error-icon{fill:#552222;}#mermaid-svg-mEbdZBrko0HvkOMb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mEbdZBrko0HvkOMb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mEbdZBrko0HvkOMb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mEbdZBrko0HvkOMb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mEbdZBrko0HvkOMb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mEbdZBrko0HvkOMb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mEbdZBrko0HvkOMb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mEbdZBrko0HvkOMb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mEbdZBrko0HvkOMb .marker.cross{stroke:#333333;}#mermaid-svg-mEbdZBrko0HvkOMb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mEbdZBrko0HvkOMb p{margin:0;}#mermaid-svg-mEbdZBrko0HvkOMb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-mEbdZBrko0HvkOMb .cluster-label text{fill:#333;}#mermaid-svg-mEbdZBrko0HvkOMb .cluster-label span{color:#333;}#mermaid-svg-mEbdZBrko0HvkOMb .cluster-label span p{background-color:transparent;}#mermaid-svg-mEbdZBrko0HvkOMb .label text,#mermaid-svg-mEbdZBrko0HvkOMb span{fill:#333;color:#333;}#mermaid-svg-mEbdZBrko0HvkOMb .node rect,#mermaid-svg-mEbdZBrko0HvkOMb .node circle,#mermaid-svg-mEbdZBrko0HvkOMb .node ellipse,#mermaid-svg-mEbdZBrko0HvkOMb .node polygon,#mermaid-svg-mEbdZBrko0HvkOMb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-mEbdZBrko0HvkOMb .rough-node .label text,#mermaid-svg-mEbdZBrko0HvkOMb .node .label text,#mermaid-svg-mEbdZBrko0HvkOMb .image-shape .label,#mermaid-svg-mEbdZBrko0HvkOMb .icon-shape .label{text-anchor:middle;}#mermaid-svg-mEbdZBrko0HvkOMb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-mEbdZBrko0HvkOMb .rough-node .label,#mermaid-svg-mEbdZBrko0HvkOMb .node .label,#mermaid-svg-mEbdZBrko0HvkOMb .image-shape .label,#mermaid-svg-mEbdZBrko0HvkOMb .icon-shape .label{text-align:center;}#mermaid-svg-mEbdZBrko0HvkOMb .node.clickable{cursor:pointer;}#mermaid-svg-mEbdZBrko0HvkOMb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-mEbdZBrko0HvkOMb .arrowheadPath{fill:#333333;}#mermaid-svg-mEbdZBrko0HvkOMb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-mEbdZBrko0HvkOMb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-mEbdZBrko0HvkOMb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mEbdZBrko0HvkOMb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-mEbdZBrko0HvkOMb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mEbdZBrko0HvkOMb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-mEbdZBrko0HvkOMb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-mEbdZBrko0HvkOMb .cluster text{fill:#333;}#mermaid-svg-mEbdZBrko0HvkOMb .cluster span{color:#333;}#mermaid-svg-mEbdZBrko0HvkOMb div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-mEbdZBrko0HvkOMb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-mEbdZBrko0HvkOMb rect.text{fill:none;stroke-width:0;}#mermaid-svg-mEbdZBrko0HvkOMb .icon-shape,#mermaid-svg-mEbdZBrko0HvkOMb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-mEbdZBrko0HvkOMb .icon-shape p,#mermaid-svg-mEbdZBrko0HvkOMb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-mEbdZBrko0HvkOMb .icon-shape .label rect,#mermaid-svg-mEbdZBrko0HvkOMb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-mEbdZBrko0HvkOMb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-mEbdZBrko0HvkOMb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-mEbdZBrko0HvkOMb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ModeNvml
ModeWsl
ModeCSV
ModeManagement
ModeGdrcopy/Gds/Mofed/Nvswitch
ModeImex
New(opts...)
mode
nvmllib
wsllib
csvlib
managementlib
gatedlib
imexlib
发现: NVML API

设备: Full GPU + MIG

公共: 控制设备+图形+驱动
发现: dxcore API

设备: /dev/dxg

公共: WSL驱动存储
发现: CSV文件 + NVML(混合)

设备: iGPU/dGPU区分

公共: CSV驱动+CUDA兼容
发现: 字符设备通配符

设备: 全部nvidia*设备

公共: 驱动库
发现: 专用Discoverer

设备: 单个'all'设备

公共: 空(None{})
发现: 字符设备通道

设备: 每个通道一个Spec

公共: 空(None{})

4.2 平台库对比表

特性 nvmllib wsllib csvlib managementlib gatedlib imexlib
设备发现 NVML API /dev/dxg CSV文件 /dev/nvidia* 专用 /dev/nvidia-caps-imex-channels
设备类型 Full GPU + MIG DXG设备 iGPU/dGPU 全部NVIDIA 模式相关 IMEX通道
设备Spec 每GPU一个 单个"all" 每设备一个 单个"all" 单个"all" 每通道一个
公共编辑 控制+图形+驱动 WSL驱动 CSV驱动 驱动库
MIG支持
设备命名 Index/UUID 固定"all" Index/UUID 固定"all" 固定"all" 通道ID
CUDA兼容 ✅(Tegra)
vendor nvidia.com nvidia.com nvidia.com management.nvidia.com nvidia.com nvidia.com
class gpu gpu gpu gpu 模式名 imex-channel

五、Mermaid图表汇总

图表1:NVCDI模块类图

(见 2.2 节)

图表2:New()工厂流程图

(见 3.2.2 节)

图表3:七种平台库策略分发图

(见 4.1 节)

图表4:GetSpec()流程图

(见 3.5 节)

图表5:Transform管道流程图

#mermaid-svg-3feKP35aHmaNFVQO{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-3feKP35aHmaNFVQO .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3feKP35aHmaNFVQO .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3feKP35aHmaNFVQO .error-icon{fill:#552222;}#mermaid-svg-3feKP35aHmaNFVQO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3feKP35aHmaNFVQO .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3feKP35aHmaNFVQO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3feKP35aHmaNFVQO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3feKP35aHmaNFVQO .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3feKP35aHmaNFVQO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3feKP35aHmaNFVQO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3feKP35aHmaNFVQO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3feKP35aHmaNFVQO .marker.cross{stroke:#333333;}#mermaid-svg-3feKP35aHmaNFVQO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3feKP35aHmaNFVQO p{margin:0;}#mermaid-svg-3feKP35aHmaNFVQO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3feKP35aHmaNFVQO .cluster-label text{fill:#333;}#mermaid-svg-3feKP35aHmaNFVQO .cluster-label span{color:#333;}#mermaid-svg-3feKP35aHmaNFVQO .cluster-label span p{background-color:transparent;}#mermaid-svg-3feKP35aHmaNFVQO .label text,#mermaid-svg-3feKP35aHmaNFVQO span{fill:#333;color:#333;}#mermaid-svg-3feKP35aHmaNFVQO .node rect,#mermaid-svg-3feKP35aHmaNFVQO .node circle,#mermaid-svg-3feKP35aHmaNFVQO .node ellipse,#mermaid-svg-3feKP35aHmaNFVQO .node polygon,#mermaid-svg-3feKP35aHmaNFVQO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3feKP35aHmaNFVQO .rough-node .label text,#mermaid-svg-3feKP35aHmaNFVQO .node .label text,#mermaid-svg-3feKP35aHmaNFVQO .image-shape .label,#mermaid-svg-3feKP35aHmaNFVQO .icon-shape .label{text-anchor:middle;}#mermaid-svg-3feKP35aHmaNFVQO .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3feKP35aHmaNFVQO .rough-node .label,#mermaid-svg-3feKP35aHmaNFVQO .node .label,#mermaid-svg-3feKP35aHmaNFVQO .image-shape .label,#mermaid-svg-3feKP35aHmaNFVQO .icon-shape .label{text-align:center;}#mermaid-svg-3feKP35aHmaNFVQO .node.clickable{cursor:pointer;}#mermaid-svg-3feKP35aHmaNFVQO .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3feKP35aHmaNFVQO .arrowheadPath{fill:#333333;}#mermaid-svg-3feKP35aHmaNFVQO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3feKP35aHmaNFVQO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3feKP35aHmaNFVQO .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3feKP35aHmaNFVQO .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3feKP35aHmaNFVQO .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3feKP35aHmaNFVQO .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3feKP35aHmaNFVQO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3feKP35aHmaNFVQO .cluster text{fill:#333;}#mermaid-svg-3feKP35aHmaNFVQO .cluster span{color:#333;}#mermaid-svg-3feKP35aHmaNFVQO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-3feKP35aHmaNFVQO .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3feKP35aHmaNFVQO rect.text{fill:none;stroke-width:0;}#mermaid-svg-3feKP35aHmaNFVQO .icon-shape,#mermaid-svg-3feKP35aHmaNFVQO .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3feKP35aHmaNFVQO .icon-shape p,#mermaid-svg-3feKP35aHmaNFVQO .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3feKP35aHmaNFVQO .icon-shape .label rect,#mermaid-svg-3feKP35aHmaNFVQO .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3feKP35aHmaNFVQO .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3feKP35aHmaNFVQO .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3feKP35aHmaNFVQO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 原始Spec
dedupe

去重
simplify

简化(移除公共实体)
sorter

排序
mergedDevice

合并设备(可选)
setMinimumVersion

设置最小版本
最终Spec
NewSimplifier()

图表6:deduplicate流程图

#mermaid-svg-Q3ZBztHIRK1ZbzQE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .error-icon{fill:#552222;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .marker.cross{stroke:#333333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Q3ZBztHIRK1ZbzQE p{margin:0;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .cluster-label text{fill:#333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .cluster-label span{color:#333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .cluster-label span p{background-color:transparent;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .label text,#mermaid-svg-Q3ZBztHIRK1ZbzQE span{fill:#333;color:#333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .node rect,#mermaid-svg-Q3ZBztHIRK1ZbzQE .node circle,#mermaid-svg-Q3ZBztHIRK1ZbzQE .node ellipse,#mermaid-svg-Q3ZBztHIRK1ZbzQE .node polygon,#mermaid-svg-Q3ZBztHIRK1ZbzQE .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .rough-node .label text,#mermaid-svg-Q3ZBztHIRK1ZbzQE .node .label text,#mermaid-svg-Q3ZBztHIRK1ZbzQE .image-shape .label,#mermaid-svg-Q3ZBztHIRK1ZbzQE .icon-shape .label{text-anchor:middle;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .rough-node .label,#mermaid-svg-Q3ZBztHIRK1ZbzQE .node .label,#mermaid-svg-Q3ZBztHIRK1ZbzQE .image-shape .label,#mermaid-svg-Q3ZBztHIRK1ZbzQE .icon-shape .label{text-align:center;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .node.clickable{cursor:pointer;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .arrowheadPath{fill:#333333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Q3ZBztHIRK1ZbzQE .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q3ZBztHIRK1ZbzQE .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Q3ZBztHIRK1ZbzQE .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .cluster text{fill:#333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .cluster span{color:#333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Q3ZBztHIRK1ZbzQE rect.text{fill:none;stroke-width:0;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .icon-shape,#mermaid-svg-Q3ZBztHIRK1ZbzQE .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .icon-shape p,#mermaid-svg-Q3ZBztHIRK1ZbzQE .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .icon-shape .label rect,#mermaid-svg-Q3ZBztHIRK1ZbzQE .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Q3ZBztHIRK1ZbzQE .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Q3ZBztHIRK1ZbzQE .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Q3ZBztHIRK1ZbzQE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
dedupe.Transform(spec)
spec == nil?
return nil
去重公共编辑
去重AdditionalGIDs(排序+去重)
去重DeviceNodes(JSON ID)
去重Env(字符串ID)
去重Hooks(JSON ID)
去重Mounts(JSON ID)
遍历设备列表
对每个设备去重编辑
返回

图表7:simplify流程图

#mermaid-svg-Z6yLfA3wANtsAnfF{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Z6yLfA3wANtsAnfF .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Z6yLfA3wANtsAnfF .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Z6yLfA3wANtsAnfF .error-icon{fill:#552222;}#mermaid-svg-Z6yLfA3wANtsAnfF .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Z6yLfA3wANtsAnfF .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Z6yLfA3wANtsAnfF .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Z6yLfA3wANtsAnfF .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Z6yLfA3wANtsAnfF .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Z6yLfA3wANtsAnfF .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Z6yLfA3wANtsAnfF .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Z6yLfA3wANtsAnfF .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Z6yLfA3wANtsAnfF .marker.cross{stroke:#333333;}#mermaid-svg-Z6yLfA3wANtsAnfF svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Z6yLfA3wANtsAnfF p{margin:0;}#mermaid-svg-Z6yLfA3wANtsAnfF .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Z6yLfA3wANtsAnfF .cluster-label text{fill:#333;}#mermaid-svg-Z6yLfA3wANtsAnfF .cluster-label span{color:#333;}#mermaid-svg-Z6yLfA3wANtsAnfF .cluster-label span p{background-color:transparent;}#mermaid-svg-Z6yLfA3wANtsAnfF .label text,#mermaid-svg-Z6yLfA3wANtsAnfF span{fill:#333;color:#333;}#mermaid-svg-Z6yLfA3wANtsAnfF .node rect,#mermaid-svg-Z6yLfA3wANtsAnfF .node circle,#mermaid-svg-Z6yLfA3wANtsAnfF .node ellipse,#mermaid-svg-Z6yLfA3wANtsAnfF .node polygon,#mermaid-svg-Z6yLfA3wANtsAnfF .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Z6yLfA3wANtsAnfF .rough-node .label text,#mermaid-svg-Z6yLfA3wANtsAnfF .node .label text,#mermaid-svg-Z6yLfA3wANtsAnfF .image-shape .label,#mermaid-svg-Z6yLfA3wANtsAnfF .icon-shape .label{text-anchor:middle;}#mermaid-svg-Z6yLfA3wANtsAnfF .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Z6yLfA3wANtsAnfF .rough-node .label,#mermaid-svg-Z6yLfA3wANtsAnfF .node .label,#mermaid-svg-Z6yLfA3wANtsAnfF .image-shape .label,#mermaid-svg-Z6yLfA3wANtsAnfF .icon-shape .label{text-align:center;}#mermaid-svg-Z6yLfA3wANtsAnfF .node.clickable{cursor:pointer;}#mermaid-svg-Z6yLfA3wANtsAnfF .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Z6yLfA3wANtsAnfF .arrowheadPath{fill:#333333;}#mermaid-svg-Z6yLfA3wANtsAnfF .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Z6yLfA3wANtsAnfF .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Z6yLfA3wANtsAnfF .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Z6yLfA3wANtsAnfF .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Z6yLfA3wANtsAnfF .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Z6yLfA3wANtsAnfF .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Z6yLfA3wANtsAnfF .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Z6yLfA3wANtsAnfF .cluster text{fill:#333;}#mermaid-svg-Z6yLfA3wANtsAnfF .cluster span{color:#333;}#mermaid-svg-Z6yLfA3wANtsAnfF div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Z6yLfA3wANtsAnfF .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Z6yLfA3wANtsAnfF rect.text{fill:none;stroke-width:0;}#mermaid-svg-Z6yLfA3wANtsAnfF .icon-shape,#mermaid-svg-Z6yLfA3wANtsAnfF .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Z6yLfA3wANtsAnfF .icon-shape p,#mermaid-svg-Z6yLfA3wANtsAnfF .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Z6yLfA3wANtsAnfF .icon-shape .label rect,#mermaid-svg-Z6yLfA3wANtsAnfF .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Z6yLfA3wANtsAnfF .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Z6yLfA3wANtsAnfF .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Z6yLfA3wANtsAnfF :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
No
simplify.Transform(spec)
先执行dedupe
获取公共编辑的实体ID集合
创建remover(commonEntityIDs)
遍历每个设备
从设备编辑中移除公共实体
设备编辑是否为空?
保持原编辑(避免无效设备)
更新设备编辑
还有更多设备?
返回

图表8:merged-device流程图

#mermaid-svg-WWI4VULMTcFdhsBU{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WWI4VULMTcFdhsBU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WWI4VULMTcFdhsBU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WWI4VULMTcFdhsBU .error-icon{fill:#552222;}#mermaid-svg-WWI4VULMTcFdhsBU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WWI4VULMTcFdhsBU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WWI4VULMTcFdhsBU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WWI4VULMTcFdhsBU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WWI4VULMTcFdhsBU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WWI4VULMTcFdhsBU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WWI4VULMTcFdhsBU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WWI4VULMTcFdhsBU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WWI4VULMTcFdhsBU .marker.cross{stroke:#333333;}#mermaid-svg-WWI4VULMTcFdhsBU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WWI4VULMTcFdhsBU p{margin:0;}#mermaid-svg-WWI4VULMTcFdhsBU .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-WWI4VULMTcFdhsBU .cluster-label text{fill:#333;}#mermaid-svg-WWI4VULMTcFdhsBU .cluster-label span{color:#333;}#mermaid-svg-WWI4VULMTcFdhsBU .cluster-label span p{background-color:transparent;}#mermaid-svg-WWI4VULMTcFdhsBU .label text,#mermaid-svg-WWI4VULMTcFdhsBU span{fill:#333;color:#333;}#mermaid-svg-WWI4VULMTcFdhsBU .node rect,#mermaid-svg-WWI4VULMTcFdhsBU .node circle,#mermaid-svg-WWI4VULMTcFdhsBU .node ellipse,#mermaid-svg-WWI4VULMTcFdhsBU .node polygon,#mermaid-svg-WWI4VULMTcFdhsBU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WWI4VULMTcFdhsBU .rough-node .label text,#mermaid-svg-WWI4VULMTcFdhsBU .node .label text,#mermaid-svg-WWI4VULMTcFdhsBU .image-shape .label,#mermaid-svg-WWI4VULMTcFdhsBU .icon-shape .label{text-anchor:middle;}#mermaid-svg-WWI4VULMTcFdhsBU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WWI4VULMTcFdhsBU .rough-node .label,#mermaid-svg-WWI4VULMTcFdhsBU .node .label,#mermaid-svg-WWI4VULMTcFdhsBU .image-shape .label,#mermaid-svg-WWI4VULMTcFdhsBU .icon-shape .label{text-align:center;}#mermaid-svg-WWI4VULMTcFdhsBU .node.clickable{cursor:pointer;}#mermaid-svg-WWI4VULMTcFdhsBU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WWI4VULMTcFdhsBU .arrowheadPath{fill:#333333;}#mermaid-svg-WWI4VULMTcFdhsBU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WWI4VULMTcFdhsBU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WWI4VULMTcFdhsBU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WWI4VULMTcFdhsBU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WWI4VULMTcFdhsBU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WWI4VULMTcFdhsBU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WWI4VULMTcFdhsBU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WWI4VULMTcFdhsBU .cluster text{fill:#333;}#mermaid-svg-WWI4VULMTcFdhsBU .cluster span{color:#333;}#mermaid-svg-WWI4VULMTcFdhsBU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WWI4VULMTcFdhsBU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WWI4VULMTcFdhsBU rect.text{fill:none;stroke-width:0;}#mermaid-svg-WWI4VULMTcFdhsBU .icon-shape,#mermaid-svg-WWI4VULMTcFdhsBU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WWI4VULMTcFdhsBU .icon-shape p,#mermaid-svg-WWI4VULMTcFdhsBU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WWI4VULMTcFdhsBU .icon-shape .label rect,#mermaid-svg-WWI4VULMTcFdhsBU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WWI4VULMTcFdhsBU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WWI4VULMTcFdhsBU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WWI4VULMTcFdhsBU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
Yes
No
No
mergedDevice.Transform(spec)
mergeDeviceSpecs(devices, name)
名为name的设备已存在?
skipIfExists?
return nil
return error: device exists
合并所有设备编辑
创建合并设备
追加到spec.Devices
执行simplifier.Transform
return nil

图表9:sorter流程图

#mermaid-svg-66dY9j2LBWklIY51{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-66dY9j2LBWklIY51 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-66dY9j2LBWklIY51 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-66dY9j2LBWklIY51 .error-icon{fill:#552222;}#mermaid-svg-66dY9j2LBWklIY51 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-66dY9j2LBWklIY51 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-66dY9j2LBWklIY51 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-66dY9j2LBWklIY51 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-66dY9j2LBWklIY51 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-66dY9j2LBWklIY51 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-66dY9j2LBWklIY51 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-66dY9j2LBWklIY51 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-66dY9j2LBWklIY51 .marker.cross{stroke:#333333;}#mermaid-svg-66dY9j2LBWklIY51 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-66dY9j2LBWklIY51 p{margin:0;}#mermaid-svg-66dY9j2LBWklIY51 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-66dY9j2LBWklIY51 .cluster-label text{fill:#333;}#mermaid-svg-66dY9j2LBWklIY51 .cluster-label span{color:#333;}#mermaid-svg-66dY9j2LBWklIY51 .cluster-label span p{background-color:transparent;}#mermaid-svg-66dY9j2LBWklIY51 .label text,#mermaid-svg-66dY9j2LBWklIY51 span{fill:#333;color:#333;}#mermaid-svg-66dY9j2LBWklIY51 .node rect,#mermaid-svg-66dY9j2LBWklIY51 .node circle,#mermaid-svg-66dY9j2LBWklIY51 .node ellipse,#mermaid-svg-66dY9j2LBWklIY51 .node polygon,#mermaid-svg-66dY9j2LBWklIY51 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-66dY9j2LBWklIY51 .rough-node .label text,#mermaid-svg-66dY9j2LBWklIY51 .node .label text,#mermaid-svg-66dY9j2LBWklIY51 .image-shape .label,#mermaid-svg-66dY9j2LBWklIY51 .icon-shape .label{text-anchor:middle;}#mermaid-svg-66dY9j2LBWklIY51 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-66dY9j2LBWklIY51 .rough-node .label,#mermaid-svg-66dY9j2LBWklIY51 .node .label,#mermaid-svg-66dY9j2LBWklIY51 .image-shape .label,#mermaid-svg-66dY9j2LBWklIY51 .icon-shape .label{text-align:center;}#mermaid-svg-66dY9j2LBWklIY51 .node.clickable{cursor:pointer;}#mermaid-svg-66dY9j2LBWklIY51 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-66dY9j2LBWklIY51 .arrowheadPath{fill:#333333;}#mermaid-svg-66dY9j2LBWklIY51 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-66dY9j2LBWklIY51 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-66dY9j2LBWklIY51 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-66dY9j2LBWklIY51 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-66dY9j2LBWklIY51 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-66dY9j2LBWklIY51 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-66dY9j2LBWklIY51 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-66dY9j2LBWklIY51 .cluster text{fill:#333;}#mermaid-svg-66dY9j2LBWklIY51 .cluster span{color:#333;}#mermaid-svg-66dY9j2LBWklIY51 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-66dY9j2LBWklIY51 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-66dY9j2LBWklIY51 rect.text{fill:none;stroke-width:0;}#mermaid-svg-66dY9j2LBWklIY51 .icon-shape,#mermaid-svg-66dY9j2LBWklIY51 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-66dY9j2LBWklIY51 .icon-shape p,#mermaid-svg-66dY9j2LBWklIY51 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-66dY9j2LBWklIY51 .icon-shape .label rect,#mermaid-svg-66dY9j2LBWklIY51 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-66dY9j2LBWklIY51 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-66dY9j2LBWklIY51 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-66dY9j2LBWklIY51 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
sorter.Transform(spec)
排序公共编辑
排序DeviceNodes(按路径深度+名称)
排序Mounts(按容器路径深度+名称)
遍历设备
排序每个设备的编辑
还有更多设备?
按名称排序设备列表
return nil

图表10:root transform流程图

#mermaid-svg-VuAllD0ykOBrkgNl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VuAllD0ykOBrkgNl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VuAllD0ykOBrkgNl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VuAllD0ykOBrkgNl .error-icon{fill:#552222;}#mermaid-svg-VuAllD0ykOBrkgNl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VuAllD0ykOBrkgNl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VuAllD0ykOBrkgNl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VuAllD0ykOBrkgNl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VuAllD0ykOBrkgNl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VuAllD0ykOBrkgNl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VuAllD0ykOBrkgNl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VuAllD0ykOBrkgNl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VuAllD0ykOBrkgNl .marker.cross{stroke:#333333;}#mermaid-svg-VuAllD0ykOBrkgNl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VuAllD0ykOBrkgNl p{margin:0;}#mermaid-svg-VuAllD0ykOBrkgNl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VuAllD0ykOBrkgNl .cluster-label text{fill:#333;}#mermaid-svg-VuAllD0ykOBrkgNl .cluster-label span{color:#333;}#mermaid-svg-VuAllD0ykOBrkgNl .cluster-label span p{background-color:transparent;}#mermaid-svg-VuAllD0ykOBrkgNl .label text,#mermaid-svg-VuAllD0ykOBrkgNl span{fill:#333;color:#333;}#mermaid-svg-VuAllD0ykOBrkgNl .node rect,#mermaid-svg-VuAllD0ykOBrkgNl .node circle,#mermaid-svg-VuAllD0ykOBrkgNl .node ellipse,#mermaid-svg-VuAllD0ykOBrkgNl .node polygon,#mermaid-svg-VuAllD0ykOBrkgNl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VuAllD0ykOBrkgNl .rough-node .label text,#mermaid-svg-VuAllD0ykOBrkgNl .node .label text,#mermaid-svg-VuAllD0ykOBrkgNl .image-shape .label,#mermaid-svg-VuAllD0ykOBrkgNl .icon-shape .label{text-anchor:middle;}#mermaid-svg-VuAllD0ykOBrkgNl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VuAllD0ykOBrkgNl .rough-node .label,#mermaid-svg-VuAllD0ykOBrkgNl .node .label,#mermaid-svg-VuAllD0ykOBrkgNl .image-shape .label,#mermaid-svg-VuAllD0ykOBrkgNl .icon-shape .label{text-align:center;}#mermaid-svg-VuAllD0ykOBrkgNl .node.clickable{cursor:pointer;}#mermaid-svg-VuAllD0ykOBrkgNl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VuAllD0ykOBrkgNl .arrowheadPath{fill:#333333;}#mermaid-svg-VuAllD0ykOBrkgNl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VuAllD0ykOBrkgNl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VuAllD0ykOBrkgNl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VuAllD0ykOBrkgNl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VuAllD0ykOBrkgNl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VuAllD0ykOBrkgNl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VuAllD0ykOBrkgNl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VuAllD0ykOBrkgNl .cluster text{fill:#333;}#mermaid-svg-VuAllD0ykOBrkgNl .cluster span{color:#333;}#mermaid-svg-VuAllD0ykOBrkgNl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VuAllD0ykOBrkgNl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VuAllD0ykOBrkgNl rect.text{fill:none;stroke-width:0;}#mermaid-svg-VuAllD0ykOBrkgNl .icon-shape,#mermaid-svg-VuAllD0ykOBrkgNl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VuAllD0ykOBrkgNl .icon-shape p,#mermaid-svg-VuAllD0ykOBrkgNl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VuAllD0ykOBrkgNl .icon-shape .label rect,#mermaid-svg-VuAllD0ykOBrkgNl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VuAllD0ykOBrkgNl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VuAllD0ykOBrkgNl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VuAllD0ykOBrkgNl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
No
root.New(opts...)
root == targetRoot?
返回noop.New()
relativeTo == 'container'?
containerRootTransformer
hostRootTransformer
转换容器路径:

DeviceNode.Path

Mount.ContainerPath

Hook.Path(仅startContainer)
转换宿主机路径:

DeviceNode.HostPath

Mount.HostPath

Hook.Path(非startContainer)

Hook.Args(含::的路径)

图表11:CDI spec生成完整流程图

#mermaid-svg-CxwxSCoDJ16qK6kl{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CxwxSCoDJ16qK6kl .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CxwxSCoDJ16qK6kl .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CxwxSCoDJ16qK6kl .error-icon{fill:#552222;}#mermaid-svg-CxwxSCoDJ16qK6kl .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CxwxSCoDJ16qK6kl .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CxwxSCoDJ16qK6kl .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CxwxSCoDJ16qK6kl .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CxwxSCoDJ16qK6kl .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CxwxSCoDJ16qK6kl .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CxwxSCoDJ16qK6kl .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CxwxSCoDJ16qK6kl .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CxwxSCoDJ16qK6kl .marker.cross{stroke:#333333;}#mermaid-svg-CxwxSCoDJ16qK6kl svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CxwxSCoDJ16qK6kl p{margin:0;}#mermaid-svg-CxwxSCoDJ16qK6kl .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-CxwxSCoDJ16qK6kl .cluster-label text{fill:#333;}#mermaid-svg-CxwxSCoDJ16qK6kl .cluster-label span{color:#333;}#mermaid-svg-CxwxSCoDJ16qK6kl .cluster-label span p{background-color:transparent;}#mermaid-svg-CxwxSCoDJ16qK6kl .label text,#mermaid-svg-CxwxSCoDJ16qK6kl span{fill:#333;color:#333;}#mermaid-svg-CxwxSCoDJ16qK6kl .node rect,#mermaid-svg-CxwxSCoDJ16qK6kl .node circle,#mermaid-svg-CxwxSCoDJ16qK6kl .node ellipse,#mermaid-svg-CxwxSCoDJ16qK6kl .node polygon,#mermaid-svg-CxwxSCoDJ16qK6kl .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-CxwxSCoDJ16qK6kl .rough-node .label text,#mermaid-svg-CxwxSCoDJ16qK6kl .node .label text,#mermaid-svg-CxwxSCoDJ16qK6kl .image-shape .label,#mermaid-svg-CxwxSCoDJ16qK6kl .icon-shape .label{text-anchor:middle;}#mermaid-svg-CxwxSCoDJ16qK6kl .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-CxwxSCoDJ16qK6kl .rough-node .label,#mermaid-svg-CxwxSCoDJ16qK6kl .node .label,#mermaid-svg-CxwxSCoDJ16qK6kl .image-shape .label,#mermaid-svg-CxwxSCoDJ16qK6kl .icon-shape .label{text-align:center;}#mermaid-svg-CxwxSCoDJ16qK6kl .node.clickable{cursor:pointer;}#mermaid-svg-CxwxSCoDJ16qK6kl .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-CxwxSCoDJ16qK6kl .arrowheadPath{fill:#333333;}#mermaid-svg-CxwxSCoDJ16qK6kl .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-CxwxSCoDJ16qK6kl .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-CxwxSCoDJ16qK6kl .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CxwxSCoDJ16qK6kl .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-CxwxSCoDJ16qK6kl .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CxwxSCoDJ16qK6kl .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-CxwxSCoDJ16qK6kl .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-CxwxSCoDJ16qK6kl .cluster text{fill:#333;}#mermaid-svg-CxwxSCoDJ16qK6kl .cluster span{color:#333;}#mermaid-svg-CxwxSCoDJ16qK6kl div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-CxwxSCoDJ16qK6kl .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-CxwxSCoDJ16qK6kl rect.text{fill:none;stroke-width:0;}#mermaid-svg-CxwxSCoDJ16qK6kl .icon-shape,#mermaid-svg-CxwxSCoDJ16qK6kl .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-CxwxSCoDJ16qK6kl .icon-shape p,#mermaid-svg-CxwxSCoDJ16qK6kl .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-CxwxSCoDJ16qK6kl .icon-shape .label rect,#mermaid-svg-CxwxSCoDJ16qK6kl .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-CxwxSCoDJ16qK6kl .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-CxwxSCoDJ16qK6kl .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-CxwxSCoDJ16qK6kl :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} nvidia-ctk cdi generate
nvcdi.New(opts...)
populateOptions()
resolveMode()
选择平台库factory
创建wrapper
wrapper.GetSpec(devices...)
factory.DeviceSpecGenerators(devices...)
平台库创建DeviceSpecGenerator
generator.GetDeviceSpecs()
deviceSpecs \[\]specs.Device
factory.GetCommonEdits()
edits *cdi.ContainerEdits
添加 NVIDIA_VISIBLE_DEVICES=void
spec.New()
builder.Build()
构建 raw *cdi.Spec
NewSimplifier().Transform()
dedupe → simplify → sorter
mergedDevice.Transform()(如果配置)
返回 spec.Interface
spec.Save(path)
setMinimumRequiredVersion.Transform()
cdi.Cache.WriteSpec()
chmod设置权限
CDI Spec文件生成完成

图表12:vendor/class/kind命名规范图

#mermaid-svg-ZPHIqm7v4hBsGA1c{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZPHIqm7v4hBsGA1c .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZPHIqm7v4hBsGA1c .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZPHIqm7v4hBsGA1c .error-icon{fill:#552222;}#mermaid-svg-ZPHIqm7v4hBsGA1c .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZPHIqm7v4hBsGA1c .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZPHIqm7v4hBsGA1c .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZPHIqm7v4hBsGA1c .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZPHIqm7v4hBsGA1c .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZPHIqm7v4hBsGA1c .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZPHIqm7v4hBsGA1c .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZPHIqm7v4hBsGA1c .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZPHIqm7v4hBsGA1c .marker.cross{stroke:#333333;}#mermaid-svg-ZPHIqm7v4hBsGA1c svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZPHIqm7v4hBsGA1c p{margin:0;}#mermaid-svg-ZPHIqm7v4hBsGA1c .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZPHIqm7v4hBsGA1c .cluster-label text{fill:#333;}#mermaid-svg-ZPHIqm7v4hBsGA1c .cluster-label span{color:#333;}#mermaid-svg-ZPHIqm7v4hBsGA1c .cluster-label span p{background-color:transparent;}#mermaid-svg-ZPHIqm7v4hBsGA1c .label text,#mermaid-svg-ZPHIqm7v4hBsGA1c span{fill:#333;color:#333;}#mermaid-svg-ZPHIqm7v4hBsGA1c .node rect,#mermaid-svg-ZPHIqm7v4hBsGA1c .node circle,#mermaid-svg-ZPHIqm7v4hBsGA1c .node ellipse,#mermaid-svg-ZPHIqm7v4hBsGA1c .node polygon,#mermaid-svg-ZPHIqm7v4hBsGA1c .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZPHIqm7v4hBsGA1c .rough-node .label text,#mermaid-svg-ZPHIqm7v4hBsGA1c .node .label text,#mermaid-svg-ZPHIqm7v4hBsGA1c .image-shape .label,#mermaid-svg-ZPHIqm7v4hBsGA1c .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZPHIqm7v4hBsGA1c .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZPHIqm7v4hBsGA1c .rough-node .label,#mermaid-svg-ZPHIqm7v4hBsGA1c .node .label,#mermaid-svg-ZPHIqm7v4hBsGA1c .image-shape .label,#mermaid-svg-ZPHIqm7v4hBsGA1c .icon-shape .label{text-align:center;}#mermaid-svg-ZPHIqm7v4hBsGA1c .node.clickable{cursor:pointer;}#mermaid-svg-ZPHIqm7v4hBsGA1c .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZPHIqm7v4hBsGA1c .arrowheadPath{fill:#333333;}#mermaid-svg-ZPHIqm7v4hBsGA1c .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZPHIqm7v4hBsGA1c .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZPHIqm7v4hBsGA1c .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZPHIqm7v4hBsGA1c .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZPHIqm7v4hBsGA1c .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZPHIqm7v4hBsGA1c .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZPHIqm7v4hBsGA1c .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZPHIqm7v4hBsGA1c .cluster text{fill:#333;}#mermaid-svg-ZPHIqm7v4hBsGA1c .cluster span{color:#333;}#mermaid-svg-ZPHIqm7v4hBsGA1c div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ZPHIqm7v4hBsGA1c .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZPHIqm7v4hBsGA1c rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZPHIqm7v4hBsGA1c .icon-shape,#mermaid-svg-ZPHIqm7v4hBsGA1c .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZPHIqm7v4hBsGA1c .icon-shape p,#mermaid-svg-ZPHIqm7v4hBsGA1c .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZPHIqm7v4hBsGA1c .icon-shape .label rect,#mermaid-svg-ZPHIqm7v4hBsGA1c .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZPHIqm7v4hBsGA1c .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZPHIqm7v4hBsGA1c .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZPHIqm7v4hBsGA1c :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} class取值
vendor取值
CDI Spec Kind命名
Kind = vendor/class
nvidia.com(默认)
management.nvidia.com(管理模式)
gpu(默认)
imex-channel(IMEX模式)
gdrcopy/gds/mofed/nvswitch(门控模式)
nvidia.com/gpu
nvidia.com/imex-channel
management.nvidia.com/gpu
nvidia.com/gds
nvidia.com/mofed
nvidia.com/nvswitch
nvidia.com/gdrcopy


附录:NVCDI模块文件统计

文件 行数 核心类型 说明
api.go 82 4 接口定义
lib.go 156 4 核心库
options.go 256 2 配置选项
mode.go 99 2 模式定义
wrapper.go 104 3 包装器
namer.go 108 5 设备命名
lib-nvml.go 219 3 NVML平台
lib-wsl.go 43 1 WSL平台
lib-csv.go 336 4 CSV平台
management.go 110 2 管理模式
gated.go 72 1 门控功能
lib-imex.go 102 2 IMEX通道
common-nvml.go 52 0 NVML公共
full-gpu-nvml.go 166 1 全GPU生成
mig-device-nvml.go 119 1 MIG生成
driver-nvml.go 258 0 NVML驱动
driver-wsl.go 108 1 WSL驱动
device-wsl.go 30 0 WSL设备
workarounds-*.go 78 1 Workaround
spec/api.go 27 1 Spec接口
spec/builder.go 146 1 Spec构建器
spec/spec.go 107 1 Spec实现
spec/set-minimum-version.go 18 1 版本Transform
transform/api.go 10 1 Transform接口
transform/deduplicate.go 130 1 去重
transform/simplify.go 71 1 简化
transform/merge.go 22 1 合并
transform/merged-device.go 101 1 合并设备
transform/sorter.go 78 1 排序
transform/remove.go 73 1 移除
transform/edits.go 138 5 编辑辅助
transform/root/*.go ~300 5 Root Transform
transform/noop/no-op.go 22 1 空Transform

本文档基于 NVIDIA Container Toolkit v1.19.1 源码分析生成

分析日期:2026-07-18

分析范围:pkg/nvcdi/ 及子目录下所有 .go 文件

相关推荐
卫子miao3 小时前
如何评价当前大语言模型的记忆机制?
后端·架构
沉迷学习 日益消瘦3 小时前
01-Kubernetes 入门:概念与安装
云原生·容器·kubernetes
ARM+FPGA+AI工业主板定制专家4 小时前
国产化RK3576+FPGA架构|晶圆传输机器人高速定位+AI瑕疵检测一体化方案
fpga开发·架构·机器人·嵌入式·fpga·工控·机器人运控
星栈4 小时前
MCP 从 stdio 迁到 SSE,踩了 5 个传输层坑
人工智能·后端·架构
写代码的强哥4 小时前
云原生数据库与传统数据库相比“新”在哪里
数据库·云原生·架构
想你依然心痛4 小时前
系统存储机制深度剖析:从Win11临时文件夹设计看微软存储架构演进
microsoft·架构
还有你Y5 小时前
读懂微服务
微服务·云原生·架构
nvd115 小时前
基于 ArgoCD 优雅落地 K8s Gateway API 与 Kong 控制器(KIC)
kubernetes·gateway·argocd
沉迷学习 日益消瘦6 小时前
04-配置与扩缩
kubernetes