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生成引擎,负责:
- 发现宿主机上的NVIDIA GPU资源(设备节点、驱动库、固件等)
- 将资源信息转换为CDI(Container Device Interface)标准规范
- 生成可被容器运行时消费的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 文件