NVIDIA Container Toolkit v1.19.1 — Modifier 模块超深度分析
源码路径:internal/modifier/ 版本:v1.19.1 文档定位:Go源码架构逐行分析系列 · 第四篇
一、模块定位
1.1 模块职责
internal/modifier 模块是 NVIDIA Container Toolkit 的 OCI Spec 修改器引擎,负责修改容器配置以注入 GPU 资源。其核心职责包括:
1.2 在系统中的位置
┌─────────────────────────────────────────────────────────────────────┐
│ internal/runtime 模块 │
│ newSpecModifier() 调用 modifier.New() │
└──────────────────────────┬──────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ internal/modifier 模块 │
│ ┌──────────────┐ ┌──────────────┐ ┌─────────────────────────┐ │
│ │ factory.go │ │ mode.go │ │ list.go │ │
│ │ (Factory创建) │ │ (模式分发) │ │ (修改器列表) │ │
│ └──────┬───────┘ └──────┬───────┘ └─────────────────────────┘ │
│ │ │ │
│ ┌──────┴──────────────────┴──────────────────────────────────┐ │
│ │ 具体修改器实现 │ │
│ │ ┌────────┐ ┌─────┐ ┌─────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │stable │ │csv │ │cdi │ │hook │ │graphics │ │ │
│ │ │.go │ │.go │ │.go │ │_remover │ │.go │ │ │
│ │ └────────┘ └─────┘ └─────┘ └──────────┘ └──────────┘ │ │
│ │ ┌────────┐ ┌──────────┐ ┌─────────────────────────┐ │ │
│ │ │gated │ │discover │ │ cdi/ (子目录) │ │ │
│ │ │.go │ │.go │ │ builder/registry/spec │ │ │
│ │ └────────┘ └──────────┘ └─────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ internal/oci 模块 │
│ Spec.Modify(modifier) → modifier.Modify(spec) │
└─────────────────────────────────────────────────────────────────────┘
1.3 模块边界
| 上游 | internal/runtime | runtime 调用 modifier.New() 创建修改器 |
| 上游 | internal/oci | 定义 SpecModifier 接口 |
| 下游 | internal/discover | 设备/hook/mount 发现器 |
| 下游 | internal/edits | spec 编辑应用 |
| 下游 | internal/config/image | CUDA 镜像解析 |
| 下游 | internal/platform-support/tegra/csv | CSV 文件解析 |
| 下游 | pkg/nvcdi | CDI spec 生成 |
| 下游 | internal/lookup/root | 驱动根路径 |
| 下游 | internal/cuda | CUDA 版本检测 |
| 下游 | internal/requirements | 需求检查 |
| 下游 | tags.cncf.io/container-device-interface | CDI 标准库 |
二、模块整体结构
2.1 文件清单
| factory.go | Factory 结构体、New()、create()、Option 模式 | ~155 |
| mode.go | 模式分发、supportedModifierTypes() | ~40 |
| stable.go | stableRuntimeModifier — legacy 模式 hook 注入 | ~60 |
| csv.go | csvModifier — CSV 模式设备注入 | ~90 |
| cdi.go | cdiModifier — CDI/JIT-CDI 模式设备注入 | ~230 |
| hook_remover.go | hookRemover — 移除旧 hook | ~75 |
| graphics.go | graphicsModifier — 图形库注入 | ~65 |
| gated.go | gatedModifier — 功能门控修改器 | ~95 |
| list.go | list — 修改器列表 | ~25 |
| discover.go | discoverModifier — 基于发现器的修改器 | ~55 |
| cdi/builder.go | CDI 修改器构建器 | ~85 |
| cdi/registry.go | fromRegistry — 基于 CDI 注册表 | ~60 |
| cdi/spec.go | fromCDISpec — 基于原始 CDI spec | ~60 |
| *_test.go | 测试文件(7个) | ~500 |
2.2 四种运行时模式对比
| legacy | feature-gated + graphics + mode(stable) | 通过 nvidia-container-runtime-hook | 注入 hook |
| csv | nvidia-hook-remover + feature-gated + mode(csv) | 基于 CSV 文件生成 CDI spec | 移除旧 hook |
| cdi | nvidia-hook-remover + mode(cdi) | 使用预生成的 CDI spec | 移除旧 hook |
| jit-cdi | nvidia-hook-remover + mode(jit-cdi) | 运行时生成 CDI spec | 移除旧 hook |
2.3 SpecModifier 接口和所有实现
#mermaid-svg-w7C0J6vYuWpfcPBV{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-w7C0J6vYuWpfcPBV .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-w7C0J6vYuWpfcPBV .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-w7C0J6vYuWpfcPBV .error-icon{fill:#552222;}#mermaid-svg-w7C0J6vYuWpfcPBV .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-w7C0J6vYuWpfcPBV .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-w7C0J6vYuWpfcPBV .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-w7C0J6vYuWpfcPBV .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-w7C0J6vYuWpfcPBV .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-w7C0J6vYuWpfcPBV .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-w7C0J6vYuWpfcPBV .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-w7C0J6vYuWpfcPBV .marker{fill:#333333;stroke:#333333;}#mermaid-svg-w7C0J6vYuWpfcPBV .marker.cross{stroke:#333333;}#mermaid-svg-w7C0J6vYuWpfcPBV svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-w7C0J6vYuWpfcPBV p{margin:0;}#mermaid-svg-w7C0J6vYuWpfcPBV g.classGroup text{fill:#9370DB;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-w7C0J6vYuWpfcPBV g.classGroup text .title{font-weight:bolder;}#mermaid-svg-w7C0J6vYuWpfcPBV .cluster-label text{fill:#333;}#mermaid-svg-w7C0J6vYuWpfcPBV .cluster-label span{color:#333;}#mermaid-svg-w7C0J6vYuWpfcPBV .cluster-label span p{background-color:transparent;}#mermaid-svg-w7C0J6vYuWpfcPBV .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-w7C0J6vYuWpfcPBV .cluster text{fill:#333;}#mermaid-svg-w7C0J6vYuWpfcPBV .cluster span{color:#333;}#mermaid-svg-w7C0J6vYuWpfcPBV .nodeLabel,#mermaid-svg-w7C0J6vYuWpfcPBV .edgeLabel{color:#131300;}#mermaid-svg-w7C0J6vYuWpfcPBV .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-w7C0J6vYuWpfcPBV .label text{fill:#131300;}#mermaid-svg-w7C0J6vYuWpfcPBV .labelBkg{background:#ECECFF;}#mermaid-svg-w7C0J6vYuWpfcPBV .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-w7C0J6vYuWpfcPBV .classTitle{font-weight:bolder;}#mermaid-svg-w7C0J6vYuWpfcPBV .node rect,#mermaid-svg-w7C0J6vYuWpfcPBV .node circle,#mermaid-svg-w7C0J6vYuWpfcPBV .node ellipse,#mermaid-svg-w7C0J6vYuWpfcPBV .node polygon,#mermaid-svg-w7C0J6vYuWpfcPBV .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-w7C0J6vYuWpfcPBV .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV g.clickable{cursor:pointer;}#mermaid-svg-w7C0J6vYuWpfcPBV g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-w7C0J6vYuWpfcPBV g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-w7C0J6vYuWpfcPBV .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-w7C0J6vYuWpfcPBV .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-w7C0J6vYuWpfcPBV .dashed-line{stroke-dasharray:3;}#mermaid-svg-w7C0J6vYuWpfcPBV .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-w7C0J6vYuWpfcPBV #compositionStart,#mermaid-svg-w7C0J6vYuWpfcPBV .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV #compositionEnd,#mermaid-svg-w7C0J6vYuWpfcPBV .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV #dependencyStart,#mermaid-svg-w7C0J6vYuWpfcPBV .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV #dependencyStart,#mermaid-svg-w7C0J6vYuWpfcPBV .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV #extensionStart,#mermaid-svg-w7C0J6vYuWpfcPBV .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV #extensionEnd,#mermaid-svg-w7C0J6vYuWpfcPBV .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV #aggregationStart,#mermaid-svg-w7C0J6vYuWpfcPBV .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV #aggregationEnd,#mermaid-svg-w7C0J6vYuWpfcPBV .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV #lollipopStart,#mermaid-svg-w7C0J6vYuWpfcPBV .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV #lollipopEnd,#mermaid-svg-w7C0J6vYuWpfcPBV .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-w7C0J6vYuWpfcPBV .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-w7C0J6vYuWpfcPBV .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-w7C0J6vYuWpfcPBV .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-w7C0J6vYuWpfcPBV .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-w7C0J6vYuWpfcPBV :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
实现
实现
实现
实现
实现
实现
实现
实现
实现
实现
«interface»
SpecModifier
+Modify(*specs.Spec) : error
Factory
-logger logger.Interface
-cfg *config.Config
-driver *root.Driver
-hookCreator HookCreator
-image *image.CUDA
-runtimeMode RuntimeMode
-editsFactory edits.Factory
+New(opts …Option)(SpecModifier, error)
+Modify(*Spec) : error
+create()(SpecModifier, error)
«type []SpecModifier»
list
+Modify(*Spec) : error
stableRuntimeModifier
-logger logger.Interface
-nvidiaContainerRuntimeHookPath string
+Modify(*Spec) : error
csvModifier
+Modify(*Spec) : error
cdiModifier
+Modify(*Spec) : error
nvidiaContainerRuntimeHookRemover
-logger logger.Interface
+Modify(*Spec) : error
graphicsModifier
+Modify(*Spec) : error
discoverModifier
-logger logger.Interface
-editsFactory edits.Factory
-discoverer discover.Discover
+Modify(*Spec) : error
cdi_fromRegistry
-logger logger.Interface
-registry *cdi.Cache
-devices []string
+Modify(*Spec) : error
cdi_fromCDISpec
-cdiSpec *cdi.Spec
+Modify(*Spec) : error
2.4 核心方法清单
| New() | factory.go | 工厂构造函数,创建 Factory |
| Factory.create() | factory.go | 根据模式创建修改器列表 |
| Factory.Modify() | factory.go | Factory 自身实现 SpecModifier |
| supportedModifierTypes() | mode.go | 返回模式支持的修改器类型列表 |
| newModeModifier() | mode.go | 根据模式创建模式修改器 |
| stableRuntimeModifier.Modify() | stable.go | 注入 prestart hook |
| csvModifier | csv.go | CSV 模式入口 |
| cdiModifier | cdi.go | CDI 模式入口 |
| hookRemover.Modify() | hook_remover.go | 移除 NVIDIA hook |
| graphicsModifier | graphics.go | 图形库注入入口 |
| gatedModifier | gated.go | 功能门控入口 |
| list.Modify() | list.go | 遍历修改器列表 |
| discoverModifier.Modify() | discover.go | 应用发现器结果 |
| cdi.New() | cdi/builder.go | CDI 修改器构建入口 |
| fromRegistry.Modify() | cdi/registry.go | 通过 CDI 注册表注入 |
| fromCDISpec.Modify() | cdi/spec.go | 通过原始 CDI spec 注入 |
三、核心逻辑深度解析
3.1 factory.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/factory.go
// 职责: Factory 结构体 — 修改器的工厂和入口
// Factory 本身也实现 SpecModifier 接口,在 Modify() 中创建并应用修改器
// =============================================================================
package modifier
import (
"fmt" // 格式化错误
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/NVIDIA/nvidia-container-toolkit/api/config/v1"
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
"github.com/NVIDIA/nvidia-container-toolkit/internal/edits"
"github.com/NVIDIA/nvidia-container-toolkit/internal/info"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/lookup/root"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// factoryOptions 定义构造 Factory 时必须设置的选项。
// 这些选项在后续创建修改器时被使用。
type factoryOptions struct {
logger logger.Interface // 日志记录器
cfg *config.Config // 配置对象(包含模式、路径、功能标志等)
driver *root.Driver // 驱动根路径信息
hookCreator discover.HookCreator // Hook 创建器(用于生成生命周期 hook)
image *image.CUDA // CUDA 镜像(包含环境变量、设备列表等)
runtimeMode info.RuntimeMode // 运行时模式(legacy/csv/cdi/jit-cdi)
}
// Factory 是修改器的工厂。
// 它持有所有创建修改器需要的依赖,并根据运行时模式创建对应的修改器。
//
// Factory 自身也实现了 oci.SpecModifier 接口:
// Modify(spec) → create() → 修改器列表 → list.Modify(spec)
//
// 这种设计使得 Factory 可以作为 SpecModifier 直接使用,
// 调用者不需要知道具体创建 了哪些修改器。
type Factory struct {
factoryOptions // 嵌入工厂选项
editsFactory edits.Factory // edits 工厂,用于创建 spec 编辑
}
// 确保 Factory 实现了 oci.SpecModifier 接口
var _ oci.SpecModifier = (*Factory)(nil)
// New 是修改器的入口函数。
// 它创建一个 Factory 并验证配置。
// 返回的 oci.SpecModifier 可以直接用于修改 spec。
//
// 参数:
// opts …Option – 配置函数列表
//
// 返回:
// oci.SpecModifier – Factory 实例(作为 SpecModifier)
// error – 验证错误
//
// 调用链:
// runtime.newSpecModifier()
// → modifier.New(WithLogger, WithConfig, WithImage, WithDriver,
// WithHookCreator, WithRuntimeMode)
// → Factory.validate()
// → 返回 Factory 作为 SpecModifier
func New(opts …Option) (oci.SpecModifier, error) {
f := createFactory(opts…)
if err := f.validate(); err != nil {
return nil, err
}
return f, nil
}
// createFactory 是内部构造函数,不执行验证。
// 用于测试中需要跳过验证的场景。
//
// 处理流程:
// 1. 创建默认 Factory
// 2. 应用所有 Option
// 3. 创建 editsFactory(用于后续 spec 编辑)
func createFactory(opts …Option) *Factory {
f := &Factory{}
for _, opt := range opts {
opt(&f.factoryOptions)
}
// 创建 edits 工厂
// editsFactory 用于将发现器的结果(mount、device、hook)转换为 OCI spec 编辑
// NoAdditionalGIDsForDeviceNodes: 控制是否为设备节点添加 additional GIDs
f.editsFactory = edits.NewFactory(
edits.WithLogger(f.logger),
edits.WithNoAdditionalGIDsForDeviceNodes(f.cfg.Features.NoAdditionalGIDsForDeviceNodes.IsEnabled()),
)
return f
}
// validate 验证 Factory 的配置是否有效。
// 检查:
// 1. driver 不能为 nil(需要驱动根路径来查找设备文件)
// 2. runtimeMode 必须是有效值: legacy, csv, cdi, jit-cdi
func (f *Factory) validate() error {
if f.driver == nil {
return fmt.Errorf("a driver must be specified")
}
switch string(f.runtimeMode) {
case "":
return fmt.Errorf("a mode must be specified")
case "legacy", "csv", "jit-cdi", "cdi":
return nil
default:
return fmt.Errorf("invalid mode %q", f.runtimeMode)
}
}
// Modify 实现 oci.SpecModifier 接口。
// 在调用时创建修改器列表并依次应用到 spec。
//
// 处理流程:
// 1. 调用 create() 创建修改器列表
// 2. 修改器列表(list 类型)依次应用 Modify
//
// 参数:
// s *specs.Spec – 要修改的 OCI spec
//
// 返回:
// error – 修改过程中的错误
func (f *Factory) Modify(s *specs.Spec) error {
m, err := f.create()
if err != nil {
return err
}
return m.Modify(s)
}
// create 根据运行时模式创建修改器列表。
// 这是 Factory 的核心方法,决定了使用哪些修改器以及它们的顺序。
//
// 处理流程:
// 1. 调用 supportedModifierTypes() 获取模式支持的修改器类型列表
// 2. 遍历类型列表,创建对应的修改器
// 3. 将修改器添加到 list 中
// 4. 返回 list(实现了 SpecModifier 接口)
//
// 修改器类型的创建顺序和逻辑:
// "mode" → newModeModifier() → 根据模式创建核心修改器
// "nvidia-hook-remover" → newNvidiaContainerRuntimeHookRemover() → 清除旧 hook
// "graphics" → newGraphicsModifier() → 注入图形支持
// "feature-gated" → newFeatureGatedModifier() → 注入可选功能
//
// 注意: 未知类型会被忽略(仅记录 Debug 日志)
func (f *Factory) create() (oci.SpecModifier, error) {
var modifiers list // 修改器列表
// 获取当前模式支持的修改器类型
for _, modifierType := range supportedModifierTypes(f.runtimeMode) {
switch modifierType {
case "mode":
// 核心模式修改器:根据模式创建 stable/csv/cdi 修改器
modeModifier, err := f.newModeModifier()
if err != nil {
return nil, err
}
modifiers = append(modifiers, modeModifier)
case "nvidia-hook-remover":
// Hook 移除器:清除旧 NVIDIA hook
modifiers = append(modifiers, f.newNvidiaContainerRuntimeHookRemover())
case "graphics":
// 图形修改器:注入图形库和 DRM 设备
graphicsModifier, err := f.newGraphicsModifier()
if err != nil {
return nil, err
}
modifiers = append(modifiers, graphicsModifier)
case "feature-gated":
// 功能门控修改器:注入 GDS/MOFED/GDRCOPY/NVSWITCH
featureGatedModifier, err := f.newFeatureGatedModifier()
if err != nil {
return nil, err
}
modifiers = append(modifiers, featureGatedModifier)
default:
// 未知类型:忽略并记录
f.logger.Debugf("Ignoring unknown modifier type %q", modifierType)
}
}
// 返回修改器列表
// list 实现了 SpecModifier,会按顺序应用所有修改器
return modifiers, nil
}
// =============================================================================
// Option 函数
// =============================================================================
// Option 是配置 factoryOptions 的函数类型
type Option func(*factoryOptions)
// WithConfig 设置配置对象
func WithConfig(cfg *config.Config) Option {
return func(f *factoryOptions) {
f.cfg = cfg
}
}
// WithDriver 设置驱动根路径
func WithDriver(driver *root.Driver) Option {
return func(f *factoryOptions) {
f.driver = driver
}
}
// WithHookCreator 设置 Hook 创建器
func WithHookCreator(hookCreator discover.HookCreator) Option {
return func(f *factoryOptions) {
f.hookCreator = hookCreator
}
}
// WithImage 设置 CUDA 镜像
func WithImage(image *image.CUDA) Option {
return func(f *factoryOptions) {
f.image = image
}
}
// WithLogger 设置日志记录器
func WithLogger(logger logger.Interface) Option {
return func(f *factoryOptions) {
f.logger = logger
}
}
// WithRuntimeMode 设置运行时模式
func WithRuntimeMode(runtimeMode info.RuntimeMode) Option {
return func(f *factoryOptions) {
f.runtimeMode = runtimeMode
}
}
3.2 mode.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/mode.go
// 职责: 模式分发 — 根据运行时模式创建对应的核心修改器
// 同时定义各模式支持的修改器类型列表
// =============================================================================
package modifier
import (
"fmt" // 格式化错误
"github.com/NVIDIA/nvidia-container-toolkit/internal/info"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// newModeModifier 根据运行时模式创建核心修改器。
// 这是模式分发的核心方法,每种模式对应不同的修改策略。
//
// 模式映射:
// legacy → stableRuntimeModifier (注入 prestart hook)
// csv → csvModifier (基于 CSV 文件)
// cdi → cdiModifier (使用预生成 CDI spec)
// jit-cdi → cdiModifier (运行时生成 CDI spec,isJitCDI=true)
//
// 返回:
// oci.SpecModifier – 对应模式的修改器
// error – 如果模式无效
func (f *Factory) newModeModifier() (oci.SpecModifier, error) {
switch f.runtimeMode {
case info.LegacyRuntimeMode:
// legacy 模式: 注入 nvidia-container-runtime-hook 作为 prestart hook
// hook 在容器创建前执行 nvidia-container-cli 来配置 GPU
return f.newStableRuntimeModifier(), nil
case info.CSVRuntimeMode:
// csv 模式: 基于 CSV 文件生成内存中的 CDI spec
// CSV 文件定义了需要挂载的设备、库文件等
return f.newCSVModifier()
case info.CDIRuntimeMode, info.JitCDIRuntimeMode:
// cdi / jit-cdi 模式: 使用 CDI 规范注入设备
// cdi: 从预生成的 CDI spec 文件读取
// jit-cdi: 运行时即时生成 CDI spec
return f.newCDIModifier(f.runtimeMode == info.JitCDIRuntimeMode)
}
return nil, fmt.Errorf("invalid runtime mode: %v", f.runtimeMode)
}
// supportedModifierTypes 返回指定运行时模式支持的修改器类型列表。
// 列表的顺序决定了修改器的应用顺序。
//
// 各模式的修改器类型:
//
// legacy 模式:
// ["feature-gated", "graphics", "mode"]
// 1. feature-gated: 注入 GDS/MOFED 等可选功能
// 2. graphics: 注入图形库和 DRM 设备
// 3. mode(stable): 注入 prestart hook
//
// csv 模式:
// ["nvidia-hook-remover", "feature-gated", "mode"]
// 1. nvidia-hook-remover: 移除旧 hook(Docker 可能已注入)
// 2. feature-gated: 注入可选功能
// 3. mode(csv): 基于 CSV 文件注入设备
//
// cdi 模式:
// ["nvidia-hook-remover", "mode"]
// 1. nvidia-hook-remover: 移除旧 hook
// 2. mode(cdi): 从 CDI spec 注入设备
//
// jit-cdi 模式:
// ["nvidia-hook-remover", "mode"]
// 1. nvidia-hook-remover: 移除旧 hook
// 2. mode(jit-cdi): 运行时生成并注入 CDI 设备
func supportedModifierTypes(mode info.RuntimeMode) []string {
switch mode {
case info.CDIRuntimeMode, info.JitCDIRuntimeMode:
// CDI 模式:不需要 graphics 和 feature-gated
// 因为 CDI spec 已经包含了所有需要的修改
return []string{"nvidia-hook-remover", "mode"}
case info.CSVRuntimeMode:
// CSV 模式:需要 feature-gated 但不需要 graphics
// graphics 由 CSV 文件中的条目定义
return []string{"nvidia-hook-remover", "feature-gated", "mode"}
default:
// legacy 模式(默认): 需要 feature-gated 和 graphics
// 不需要 hook-remover(legacy 模式自己注入 hook)
return []string{"feature-gated", "graphics", "mode"}
}
}
3.3 stable.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/stable.go
// 职责: stableRuntimeModifier — legacy 模式的 hook 注入修改器
// 向 OCI spec 注入 nvidia-container-runtime-hook 作为 prestart hook
// =============================================================================
package modifier
import (
"path/filepath" // 路径处理(获取 hook 基础名)
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// newStableRuntimeModifier 创建 legacy 模式的稳定运行时修改器。
// 从 Factory 配置中提取 hook 路径。
//
// 返回的修改器行为:
// 如果 spec 中已有 NVIDIA hook → 不修改(幂等)
// 如果 spec 中没有 NVIDIA hook → 添加 prestart hook
func (f *Factory) newStableRuntimeModifier() oci.SpecModifier {
m := stableRuntimeModifier{
logger: f.logger,
nvidiaContainerRuntimeHookPath: f.cfg.NVIDIAContainerRuntimeHookConfig.Path,
// hook 路径在 runtime.Run() 中已通过 ResolveNVIDIAContainerRuntimeHookPath 解析
}
return &m
}
// stableRuntimeModifier 是 legacy 模式的核心修改器。
//
// "stable" 含义: 这是 NVIDIA Container Toolkit 最早期的修改方式,
// 通过注入 prestart hook,在容器创建前由 nvidia-container-runtime-hook
// 执行 nvidia-container-cli 来配置 GPU。
//
// 工作原理:
// 1. 检查 OCI spec 是否已有 NVIDIA hook
// 2. 如果没有,添加 nvidia-container-runtime-hook 作为 prestart hook
// 3. hook 在容器进程启动前执行,配置 GPU 设备和库文件
type stableRuntimeModifier struct {
logger logger.Interface // 日志记录器
nvidiaContainerRuntimeHookPath string // hook 二进制的完整路径
}
// Modify 向 OCI spec 注入 NVIDIA prestart hook。
//
// 处理流程:
// 1. 检查 spec.Hooks 是否已有 NVIDIA hook
// 2. 如果已有,不修改(幂等性)
// 3. 如果没有,创建/追加 prestart hook
//
// 幂等性检查:
// 遍历 spec.Hooks.Prestart,使用 isNVIDIAContainerRuntimeHook 判断
// 如果找到匹配的 hook,记录日志并返回 nil
//
// Hook 格式:
// {
// Path: nvidiaContainerRuntimeHookPath, // 如 "/usr/bin/nvidia-container-runtime-hook"
// Args: ["nvidia-container-runtime-hook", "prestart"]
// }
//
// 参数:
// spec *specs.Spec – 要修改的 OCI spec
//
// 返回:
// error – 总是 nil(此修改器不会失败)
func (m stableRuntimeModifier) Modify(spec *specs.Spec) error {
// ========================================================================
// 步骤1: 幂等性检查
// ========================================================================
// 检查是否已有 NVIDIA hook
if spec.Hooks != nil {
for _, hook := range spec.Hooks.Prestart {
hook := hook // 创建局部变量,避免取地址循环变量的问题
if isNVIDIAContainerRuntimeHook(&hook) {
// 已有 NVIDIA hook,不重复添加
m.logger.Infof("Existing nvidia prestart hook (%v) found in OCI spec", hook.Path)
return nil
}
}
}
// ========================================================================
// 步骤2: 构造并注入 hook
// ========================================================================
path := m.nvidiaContainerRuntimeHookPath
m.logger.Infof("Using prestart hook path: %v", path)
// Args 的第一个元素是 hook 程序的基础名(如 "nvidia-container-runtime-hook")
args := []string{filepath.Base(path)}
// 确保 spec.Hooks 结构存在
if spec.Hooks == nil {
spec.Hooks = &specs.Hooks{}
}
// 追加 prestart hook
// Args 包含 ["nvidia-container-runtime-hook", "prestart"]
// "prestart" 是 hook 的子命令,指示 hook 在容器进程启动前执行
spec.Hooks.Prestart = append(spec.Hooks.Prestart, specs.Hook{
Path: path, // hook 二进制的完整路径
Args: append(args, "prestart"), // 命令行参数
})
return nil
}
3.4 csv.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/csv.go
// 职责: csvModifier — CSV 模式的修改器
// 基于 CSV 文件和容器镜像信息生成内存中的 CDI spec 来修改容器配置
// =============================================================================
package modifier
import (
"fmt" // 格式化错误
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
"github.com/NVIDIA/nvidia-container-toolkit/internal/cuda"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
"github.com/NVIDIA/nvidia-container-toolkit/internal/requirements"
)
// newCSVModifier 创建 CSV 模式的修改器。
// 这是 CSV 模式的入口函数。
//
// 处理流程:
// 1. 从镜像中提取设备请求列表
// 2. 如果没有设备请求 → 返回 nil(不需要修改)
// 3. 检查 CUDA 需求(版本兼容性等)
// 4. 创建自动 CDI spec 修改器
//
// 返回:
// oci.SpecModifier – CDI spec 修改器,或 nil(无设备请求时)
// error – 需求检查失败或其他错误
func (f *Factory) newCSVModifier() (oci.SpecModifier, error) {
// ========================================================================
// 步骤1: 提取设备请求
// ========================================================================
// csvDevices 类型别名,从 f.image 中提取可见设备
// DeviceRequests() 将设备列表转换为 "mode=csv,id=XXX" 格式
// withUniqueDevices 包装器去重设备列表
devices := withUniqueDevices(csvDevices(*f.image)).DeviceRequests()
// 如果没有设备请求,不需要修改
if len(devices) == 0 {
f.logger.Infof("No modification required; no devices requested")
return nil, nil
// 返回 nil 修改器,NewModifyingRuntimeWrapper 会检测到 nil
// 并直接返回低层运行时(不修改 spec)
}
f.logger.Infof("Constructing modifier from config: %+v", *f.cfg)
// ========================================================================
// 步骤2: 检查 CUDA 需求
// ========================================================================
// checkRequirements 验证容器镜像的 CUDA 需求是否与主机兼容
if err := checkRequirements(f.logger, f.image); err != nil {
return nil, fmt.Errorf("requirements not met: %v", err)
}
// ========================================================================
// 步骤3: 创建自动 CDI spec 修改器
// ========================================================================
// newAutomaticCDISpecModifier 使用 nvcdi 库生成内存中的 CDI spec
// 并创建 CDI 修改器来应用这些修改
return f.newAutomaticCDISpecModifier(devices)
}
// checkRequirements 检查容器镜像的 CUDA 需求是否与主机兼容。
//
// 检查内容:
// 1. 如果镜像设置了 NVIDIA_DISABLE_REQUIRE → 跳过检查
// 2. 获取镜像的 CUDA 需求(从 NVIDIA_REQUIRE_* 环境变量)
// 3. 获取主机 CUDA 版本和计算能力
// 4. 断言主机满足镜像需求
//
// 参数:
// logger logger.Interface – 日志记录器
// image *image.CUDA – CUDA 镜像
//
// 返回:
// error – 如果需求不满足
func checkRequirements(logger logger.Interface, image *image.CUDA) error {
// 如果设置了 NVIDIA_DISABLE_REQUIRE 或镜像为 nil,跳过检查
if image == nil || image.HasDisableRequire() {
logger.Debugf("NVIDIA_DISABLE_REQUIRE=%v; skipping requirement checks", true)
return nil
}
// 从镜像提取 CUDA 需求
// 需求来自 NVIDIA_REQUIRE_CUDA, NVIDIA_REQUIRE_JETPACK 等环境变量
imageRequirements, err := image.GetRequirements()
if err != nil {
return fmt.Errorf("failed to get image requirements: %v", err)
}
// 创建需求检查器
r := requirements.New(logger, imageRequirements)
// 获取主机 CUDA 版本并添加到检查器
cudaVersion, err := cuda.Version()
if err != nil {
logger.Warningf("Failed to get CUDA version: %v", err)
} else {
r.AddVersionProperty(requirements.CUDA, cudaVersion)
}
// 获取主机计算能力并添加到检查器
compteCapability, err := cuda.ComputeCapability(0)
if err != nil {
logger.Warningf("Failed to get CUDA Compute Capability: %v", err)
} else {
r.AddVersionProperty(requirements.ARCH, compteCapability)
}
// 断言所有需求满足
return r.Assert()
}
// csvDevices 是 image.CUDA 的类型别名
// 实现了 DeviceRequests() 方法,将设备列表转换为 CSV 模式标识符
type csvDevices image.CUDA
// DeviceRequests 将 CUDA 镜像中的可见设备转换为 CSV 模式的设备请求格式
// 格式: "mode=csv,id=DEVICE_ID"
// 例如: ["mode=csv,id=GPU-1234", "mode=csv,id=GPU-5678"]
func (d csvDevices) DeviceRequests() []string {
var devices []string
i := (image.CUDA)(d) // 类型转换回 image.CUDA
for _, deviceID := range i.VisibleDevices() {
devices = append(devices, "mode=csv,id="+deviceID)
}
return devices
}
3.5 cdi.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/cdi.go
// 职责: cdiModifier — CDI 和 JIT-CDI 模式的修改器
// 通过 CDI (Container Device Interface) 规范注入 GPU 设备
// =============================================================================
package modifier
import (
"fmt" // 格式化
"strings" // 字符串操作
"tags.cncf.io/container-device-interface/pkg/parser" // CDI 解析器
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/modifier/cdi"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
"github.com/NVIDIA/nvidia-container-toolkit/internal/platform-support/tegra/csv"
"github.com/NVIDIA/nvidia-container-toolkit/pkg/nvcdi"
)
// 常量定义自动 CDI 设备的 vendor/class/kind
const (
automaticDeviceVendor = "runtime.nvidia.com" // 自动设备的 vendor
automaticDeviceClass = "gpu" // 自动设备的 class
automaticDeviceKind = automaticDeviceVendor + "/" + automaticDeviceClass // "runtime.nvidia.com/gpu"
automaticDevicePrefix = automaticDeviceKind + "=" // "runtime.nvidia.com/gpu="
)
// newCDIModifier 创建 CDI 模式的修改器。
// 这是 CDI 和 JIT-CDI 模式的入口函数。
//
// 参数:
// isJitCDI bool – true=JIT-CDI 模式, false=CDI 模式
//
// 处理流程:
// 1. 确定 defaultKind(JIT-CDI 使用自动 kind,CDI 使用配置的 kind)
// 2. 创建设备请求器,获取设备列表
// 3. 如果没有设备请求 → 返回 nil
// 4. 过滤自动设备(以 "runtime.nvidia.com/" 开头的)
// 5. 如果全是自动设备 → 使用 JIT CDI 修改器
// 6. 如果全是普通 CDI 设备 → 使用注册表修改器
// 7. 混合设备 → 报错(不允许)
func (f *Factory) newCDIModifier(isJitCDI bool) (oci.SpecModifier, error) {
// 确定 defaultKind
// CDI 模式: 使用配置中的 DefaultKind
// JIT-CDI 模式: 强制使用自动设备 kind "runtime.nvidia.com/gpu"
defaultKind := f.cfg.NVIDIAContainerRuntimeConfig.Modes.CDI.DefaultKind
if isJitCDI {
defaultKind = automaticDeviceKind
}
// 创建设备请求器
// newCDIDeviceRequestor 从镜像中提取设备列表
deviceRequestor := newCDIDeviceRequestor(
f.logger,
f.image,
defaultKind,
)
// 获取设备请求列表
// 设备可能来自:
// – NVIDIA_VISIBLE_DEVICES 环境变量
// – CDI annotation
// – volume mount
devices := deviceRequestor.DeviceRequests()
// 没有设备请求
if len(devices) == 0 {
f.logger.Debugf("No devices requested; no modification required.")
return nil, nil
}
// 过滤自动设备
// 自动设备是以 "runtime.nvidia.com/gpu=" 为前缀的设备名
automaticDevices := filterAutomaticDevices(devices)
// 验证:不能同时请求自动设备和其他 CDI 设备
if len(automaticDevices) != len(devices) && len(automaticDevices) > 0 {
return nil, fmt.Errorf("requesting a CDI device with vendor 'runtime.nvidia.com' is not supported when requesting other CDI devices")
}
// 全是自动设备 → 使用 JIT CDI 修改器
if len(automaticDevices) > 0 {
f.logger.Debugf("Using automatic CDI modfier for devices %v", automaticDevices)
modifier, err := f.newJitCDIModifier(automaticDevices)
if err != nil {
return nil, fmt.Errorf("failed to create the automatic CDI modifier: %w", err)
}
return modifier, nil
}
// 全是普通 CDI 设备 → 使用注册表修改器
// 从 CDI spec 目录读取预生成的 spec 文件
f.logger.Debugf("Creating CDI modifier for devices: %v", devices)
return cdi.New(
cdi.WithLogger(f.logger),
cdi.WithDevices(devices…),
cdi.WithSpecDirs(f.cfg.NVIDIAContainerRuntimeConfig.Modes.CDI.SpecDirs…),
)
}
// newJitCDIModifier 创建 JIT-CDI 修改器。
// 在运行时即时生成 CDI spec 并注入设备。
//
// 除了自动 GPU 设备外,还附加:
// – gated 设备(GDS/MOFED/GDRCOPY/NVSWITCH)
// – IMEX channel 设备
//
// 参数:
// automaticDevices []string – 自动设备列表
//
// 返回:
// oci.SpecModifier – 自动 CDI spec 修改器
// error – 错误信息
func (f *Factory) newJitCDIModifier(automaticDevices []string) (oci.SpecModifier, error) {
if f.image != nil {
// 附加 gated 设备(如果对应环境变量已设置)
automaticDevices = append(automaticDevices, withUniqueDevices(gatedDevices(*f.image)).DeviceRequests()…)
// 附加 IMEX channel 设备
automaticDevices = append(automaticDevices, withUniqueDevices(imexDevices(*f.image)).DeviceRequests()…)
}
return f.newAutomaticCDISpecModifier(automaticDevices)
}
// =============================================================================
// 设备请求器接口和实现
// =============================================================================
// deviceRequestor 是设备请求器的接口
type deviceRequestor interface {
DeviceRequests() []string
}
// cdiDeviceRequestor 从 CUDA 镜像中提取 CDI 设备请求
type cdiDeviceRequestor struct {
logger logger.Interface
image *image.CUDA
defaultKind string // 默认 CDI kind(如 "nvidia.com/gpu" 或 "runtime.nvidia.com/gpu")
}
// newCDIDeviceRequestor 创建 CDI 设备请求器
// 包装了去重功能
func newCDIDeviceRequestor(logger logger.Interface, image *image.CUDA, defaultKind string) deviceRequestor {
c := &cdiDeviceRequestor{
logger: logger,
image: image,
defaultKind: defaultKind,
}
return withUniqueDevices(c) // 包装去重器
}
// DeviceRequests 从镜像中提取设备请求列表
//
// 处理逻辑:
// 1. 获取镜像的 VisibleDevices()
// 2. 对每个设备名:
// a. 空设备名 → "none"
// b. 如果不是限定的 CDI 设备名 → 添加 defaultKind 前缀
// c. 如果已是限定的 CDI 设备名 → 直接使用
//
// 限定 CDI 设备名格式: "vendor/class=device"
// 例如: "nvidia.com/gpu=0", "runtime.nvidia.com/gpu=all"
//
// 返回示例:
// 输入: NVIDIA_VISIBLE_DEVICES=0,1
// 输出: ["nvidia.com/gpu=0", "nvidia.com/gpu=1"]
func (c *cdiDeviceRequestor) DeviceRequests() []string {
if c == nil {
return nil
}
var devices []string
for _, name := range c.image.VisibleDevices() {
if name == "" {
name = "none"
}
// parser.IsQualifiedName 检查是否为 "vendor/class=device" 格式
if !parser.IsQualifiedName(name) {
// 不是限定名,添加 defaultKind 前缀
name = fmt.Sprintf("%s=%s", c.defaultKind, name)
}
devices = append(devices, name)
}
return devices
}
// =============================================================================
// Gated 设备
// =============================================================================
// gatedDevices 是 image.CUDA 的类型别名
// 用于提取基于环境变量门控的功能设备
type gatedDevices image.CUDA
// DeviceRequests 根据 NVIDIA_* 环境变量返回功能设备请求
//
// 支持的功能:
// NVIDIA_GDS=enabled → "mode=gds"
// NVIDIA_MOFED=enabled → "mode=mofed"
// NVIDIA_GDRCOPY=enabled → "mode=gdrcopy"
// NVIDIA_NVSWITCH=enabled → "mode=nvswitch"
func (g gatedDevices) DeviceRequests() []string {
i := (image.CUDA)(g)
var devices []string
if i.Getenv("NVIDIA_GDS") == "enabled" {
devices = append(devices, "mode=gds")
}
if i.Getenv("NVIDIA_MOFED") == "enabled" {
devices = append(devices, "mode=mofed")
}
if i.Getenv("NVIDIA_GDRCOPY") == "enabled" {
devices = append(devices, "mode=gdrcopy")
}
if i.Getenv("NVIDIA_NVSWITCH") == "enabled" {
devices = append(devices, "mode=nvswitch")
}
return devices
}
// =============================================================================
// IMEX Channel 设备
// =============================================================================
// imexDevices 是 image.CUDA 的类型别名
// 用于提取 IMEX channel 设备请求
type imexDevices image.CUDA
// DeviceRequests 返回 IMEX channel 设备请求
// IMEX (Import/Export) channel 是 NVIDIA 的多 GPU 通信机制
func (d imexDevices) DeviceRequests() []string {
var devices []string
i := (image.CUDA)(d)
for _, channelID := range i.ImexChannelRequests() {
devices = append(devices, "mode=imex,id="+channelID)
}
return devices
}
// =============================================================================
// 自动设备过滤
// =============================================================================
// filterAutomaticDevices 过滤出自动设备。
// 自动设备是以 "runtime.nvidia.com/gpu=" 为前缀的 CDI 设备名。
//
// 这些设备触发 JIT CDI spec 生成,不需要预先生成 CDI spec。
//
// 参数:
// devices []string – 设备列表
//
// 返回:
// []string – 仅包含自动设备的列表
func filterAutomaticDevices(devices []string) []string {
var automatic []string
for _, device := range devices {
if !strings.HasPrefix(device, automaticDevicePrefix) {
continue
}
automatic = append(automatic, device)
}
return automatic
}
// =============================================================================
// 自动 CDI Spec 修改器
// =============================================================================
// newAutomaticCDISpecModifier 为给定设备列表生成内存中的 CDI spec 并创建修改器。
//
// 这是 JIT-CDI 模式的核心方法,在运行时即时生成 CDI spec。
//
// 处理流程:
// 1. 获取 CSV 文件列表(用于 Tegra 兼容)
// 2. 从设备列表中提取模式标识符
// 3. 对每个模式:
// a. 创建 nvcdi 库实例
// b. 生成 CDI spec
// c. 创建 CDI 修改器
// 4. 返回修改器列表
//
// 参数:
// devices []string – 设备请求列表(如 ["0", "mode=gds", "mode=imex,id=1"])
//
// 返回:
// oci.SpecModifier – SpecModifiers 列表
// error – 错误信息
func (f *Factory) newAutomaticCDISpecModifier(devices []string) (oci.SpecModifier, error) {
f.logger.Debugf("Generating in-memory CDI specs for devices %v", devices)
// 获取 CSV 文件列表(用于 Tegra 平台兼容)
csvFiles, err := csv.GetFileList(f.cfg.NVIDIAContainerRuntimeConfig.Modes.CSV.MountSpecPath)
if err != nil {
f.logger.Warningf("Failed to get the list of CSV files: %v", err)
}
// 如果不是 JetPack csv-mounts=all,只使用基础 CSV 文件
if f.image.Getenv(image.EnvVarNvidiaRequireJetpack) != "csv-mounts=all" {
csvFiles = csv.BaseFilesOnly(csvFiles)
}
// 从设备列表中提取模式标识符
// 每个设备可能是: "0" (auto), "mode=gds", "mode=csv,id=GPU0" 等
cdiModeIdentifiers := cdiModeIdentfiersFromDevices(devices…)
f.logger.Debugf("Per-mode identifiers: %v", cdiModeIdentifiers)
var modifiers oci.SpecModifiers
// 对每个模式生成 CDI spec 和修改器
for _, mode := range cdiModeIdentifiers.modes {
// 创建 nvcdi 库实例
cdilib, err := nvcdi.New(
nvcdi.WithLogger(f.logger),
nvcdi.WithNVIDIACDIHookPath(f.cfg.NVIDIACTKConfig.Path),
nvcdi.WithDriverRoot(f.driver.Root),
nvcdi.WithDevRoot(f.driver.DevRoot),
nvcdi.WithEditsFactory(f.editsFactory),
nvcdi.WithVendor(automaticDeviceVendor), // "runtime.nvidia.com"
nvcdi.WithClass(cdiModeIdentifiers.deviceClassByMode[mode]), // 设备 class
nvcdi.WithMode(mode), // 模式
nvcdi.WithFeatureFlags(f.cfg.NVIDIAContainerRuntimeConfig.Modes.JitCDI.NVCDIFeatureFlags…),
nvcdi.WithCSVCompatContainerRoot(f.cfg.NVIDIAContainerRuntimeConfig.Modes.CSV.CompatContainerRoot),
nvcdi.WithCSVFiles(csvFiles),
)
if err != nil {
return nil, fmt.Errorf("failed to construct CDI library for mode %q: %w", mode, err)
}
// 生成 CDI spec
spec, err := cdilib.GetSpec(cdiModeIdentifiers.idsByMode[mode]…)
if err != nil {
return nil, fmt.Errorf("failed to generate CDI spec for mode %q: %w", mode, err)
}
// 创建 CDI 修改器(使用生成的 spec)
cdiDeviceRequestor, err := cdi.New(
cdi.WithLogger(f.logger),
cdi.WithSpec(spec.Raw()),
)
if err != nil {
return nil, fmt.Errorf("failed to construct CDI modifier for mode %q: %w", mode, err)
}
modifiers = append(modifiers, cdiDeviceRequestor)
}
return modifiers, nil
}
// =============================================================================
// CDI 模式标识符
// =============================================================================
// cdiModeIdentifiers 存储按模式分组的设备标识符
type cdiModeIdentifiers struct {
modes []string // 模式列表(保持顺序)
idsByMode map[string][]string // 每个模式的设备 ID 列表
deviceClassByMode map[string]string // 每个模式的设备 class
}
// cdiModeIdentfiersFromDevices 从设备列表中提取模式标识符
//
// 设备格式:
// "0" → mode="auto", id="0"
// "runtime.nvidia.com/gpu=0" → mode="auto", id="0"
// "mode=csv,id=GPU0" → mode="csv", id="GPU0"
// "mode=gds" → mode="gds", id=""
// "mode=imex,id=1" → mode="imex", id="1"
//
// 默认 deviceClass: "auto" → "gpu"
func cdiModeIdentfiersFromDevices(devices …string) *cdiModeIdentifiers {
perModeIdentifiers := make(map[string][]string)
perModeDeviceClass := map[string]string{"auto": automaticDeviceClass} // "auto" → "gpu"
var uniqueModes []string
seen := make(map[string]bool)
for _, device := range devices {
mode, id := getModeIdentifier(device)
if !seen[mode] {
uniqueModes = append(uniqueModes, mode)
seen[mode] = true
}
if id != "" {
perModeIdentifiers[mode] = append(perModeIdentifiers[mode], id)
}
}
return &cdiModeIdentifiers{
modes: uniqueModes,
idsByMode: perModeIdentifiers,
deviceClassByMode: perModeDeviceClass,
}
}
// getModeIdentifier 从设备字符串中提取模式和 ID
//
// 解析规则:
// "0" → ("auto", "0") — 无 mode= 前缀,视为 auto
// "runtime.nvidia.com/gpu=0" → ("auto", "0") — 去除自动设备前缀
// "mode=csv,id=GPU0" → ("csv", "GPU0") — 提取 mode 和 id
// "mode=gds" → ("gds", "") — 只有 mode,无 id
func getModeIdentifier(device string) (string, string) {
if !strings.HasPrefix(device, "mode=") {
// 不是 "mode=" 前缀,视为 auto 模式
// 去除自动设备前缀(如果有)
return "auto", strings.TrimPrefix(device, automaticDevicePrefix)
}
// 分割 "mode=XXX,id=YYY" 或 "mode=XXX"
parts := strings.SplitN(device, ",", 2)
mode := strings.TrimPrefix(parts[0], "mode=")
if len(parts) == 2 {
return mode, strings.TrimPrefix(parts[1], "id=")
}
return mode, ""
}
// =============================================================================
// 去重包装器
// =============================================================================
// deduplicatedDeviceRequestor 包装设备请求器,提供去重功能
type deduplicatedDeviceRequestor struct {
deviceRequestor // 嵌入被包装的请求器
}
// withUniqueDevices 包装设备请求器,添加去重功能
func withUniqueDevices(deviceRequestor deviceRequestor) deviceRequestor {
return &deduplicatedDeviceRequestor{deviceRequestor: deviceRequestor}
}
// DeviceRequests 返回去重后的设备请求列表
func (d *deduplicatedDeviceRequestor) DeviceRequests() []string {
if d == nil {
return nil
}
seen := make(map[string]bool)
var devices []string
for _, device := range d.deviceRequestor.DeviceRequests() {
if seen[device] {
continue // 跳过重复设备
}
seen[device] = true
devices = append(devices, device)
}
return devices
}
3.6 hook_remover.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/hook_remover.go
// 职责: nvidiaContainerRuntimeHookRemover — 移除旧 NVIDIA hook
// 在 CDI/CSV 模式下,Docker 可能已注入旧 hook,需要先清除
// =============================================================================
package modifier
import (
"path/filepath" // 获取路径基础名
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/NVIDIA/nvidia-container-toolkit/api/config/v1"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// nvidiaContainerRuntimeHookRemover 是一个 spec 修改器,
// 检测并移除 OCI spec 中的 NVIDIA Container Runtime Hook。
//
// 使用场景:
// – CDI 模式: Docker 通过 –gpus 可能注入了旧 hook,需要移除
// – CSV 模式: 同上
// – JIT-CDI 模式: 同上
// – legacy 模式: 不需要(自己注入 hook)
//
// 移除的 hook 类型:
// – nvidia-container-runtime-hook (传统命名)
// – nvidia-container-toolkit (新命名)
type nvidiaContainerRuntimeHookRemover struct {
logger logger.Interface
}
// 确保实现 SpecModifier 接口
var _ oci.SpecModifier = (*nvidiaContainerRuntimeHookRemover)(nil)
// newNvidiaContainerRuntimeHookRemover 创建 hook 移除器
func (f *Factory) newNvidiaContainerRuntimeHookRemover() oci.SpecModifier {
return nvidiaContainerRuntimeHookRemover{
logger: f.logger,
}
}
// Modify 从 spec 中移除所有 NVIDIA hook。
//
// 处理流程:
// 1. 检查 spec 和 Hooks 是否为 nil(安全检查)
// 2. 遍历 Prestart hooks
// 3. 使用 isNVIDIAContainerRuntimeHook 判断每个 hook
// 4. 过滤掉 NVIDIA hook
// 5. 如果有变化,更新 spec.Hooks.Prestart
//
// 参数:
// spec *specs.Spec – 要修改的 spec
//
// 返回:
// error – 总是 nil(此修改器不会失败)
func (m nvidiaContainerRuntimeHookRemover) Modify(spec *specs.Spec) error {
// 安全检查
if spec == nil {
return nil
}
if spec.Hooks == nil {
return nil
}
if len(spec.Hooks.Prestart) == 0 {
return nil
}
// 构建新的 hook 列表(排除 NVIDIA hook)
var newPrestart []specs.Hook
for _, hook := range spec.Hooks.Prestart {
hook := hook // 局部变量,避免取地址循环变量
if isNVIDIAContainerRuntimeHook(&hook) {
m.logger.Debugf("Removing hook %v", hook)
continue // 跳过 NVIDIA hook
}
newPrestart = append(newPrestart, hook)
}
// 如果有变化,更新 spec
if len(newPrestart) != len(spec.Hooks.Prestart) {
m.logger.Debugf("Updating 'prestart' hooks to %v", newPrestart)
spec.Hooks.Prestart = newPrestart
}
return nil
}
// isNVIDIAContainerRuntimeHook 检查一个 hook 是否为 NVIDIA hook。
// 通过比较 hook.Path 的基础名与已知 NVIDIA 可执行文件名。
//
// 检测的可执行文件:
// "nvidia-container-runtime-hook" — 传统 hook 名称
// "nvidia-container-toolkit" — 新的工具名称
//
// 参数:
// hook *specs.Hook – 要检查的 hook
//
// 返回:
// bool – true 表示是 NVIDIA hook
func isNVIDIAContainerRuntimeHook(hook *specs.Hook) bool {
bins := map[string]struct{}{
config.NVIDIAContainerRuntimeHookExecutable: {}, // "nvidia-container-runtime-hook"
config.NVIDIAContainerToolkitExecutable: {}, // "nvidia-container-toolkit"
}
// 使用 filepath.Base 获取路径的基础名
// 例如: "/usr/bin/nvidia-container-runtime-hook" → "nvidia-container-runtime-hook"
_, exists := bins[filepath.Base(hook.Path)]
return exists
}
3.7 graphics.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/graphics.go
// 职责: graphicsModifier — 注入图形相关修改
// 当容器需要 graphics/display 能力时,注入 DRM 设备和图形库挂载
// =============================================================================
package modifier
import (
"fmt" // 格式化错误
"github.com/NVIDIA/nvidia-container-toolkit/internal/config/image"
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// newGraphicsModifier 创建图形修改器。
// 仅在 legacy 模式下使用。
//
// 处理流程:
// 1. 检查是否需要图形修改(设备 + graphics/display 能力)
// 2. 如果不需要 → 返回 nil
// 3. 创建图形挂载发现器(发现 X11、OpenGL 等库)
// 4. 创建 DRM 节点发现器(发现 /dev/dri/* 设备)
// 5. 合并发现器并创建 discoverModifier
//
// 返回:
// oci.SpecModifier – 图形修改器,或 nil
// error – 错误信息
func (f *Factory) newGraphicsModifier() (oci.SpecModifier, error) {
// 检查是否需要图形修改
devices, reason := requiresGraphicsModifier(*f.image)
if len(devices) == 0 {
f.logger.Infof("No graphics modifier required; %v", reason)
return nil, nil
}
// 创建图形挂载发现器
// 发现与图形相关的库文件和挂载点
mounts, err := discover.NewGraphicsMountsDiscoverer(
f.logger,
f.driver,
f.hookCreator,
)
if err != nil {
return nil, fmt.Errorf("failed to create mounts discoverer: %v", err)
}
// 创建 DRM 节点发现器
// 发现与指定 GPU 关联的 DRM 设备节点(/dev/dri/card*, /dev/dri/renderD***)
// devRoot 通常是驱动根路径
devRoot := f.driver.Root
drmNodes, err := discover.NewDRMNodesDiscoverer(
f.logger,
image.NewVisibleDevices(devices…),
devRoot,
f.hookCreator,
)
if err != nil {
return nil, fmt.Errorf("failed to construct discoverer: %v", err)
}
// 合并两个发现器为一个
// discover.Merge 创建一个合并的发现器,其 Hooks/Devices/Mounts 是两个发现器的并集
d := discover.Merge(
drmNodes,
mounts,
)
// 创建基于发现器的修改器
return f.newModifierFromDiscoverer(d)
}
// requiresGraphicsModifier 确定是否需要图形修改器。
//
// 条件:
// 1. 有可见设备请求(NVIDIA_VISIBLE_DEVICES 非空)
// 2. 驱动能力包含 graphics 或 display
//
// 参数:
// cudaImage image.CUDA – CUDA 镜像
//
// 返回:
// []string – 设备列表(非空表示需要图形修改)
// string – 不需要的原因(用于日志)
func requiresGraphicsModifier(cudaImage image.CUDA) ([]string, string) {
// 检查是否有可见设备
devices := cudaImage.VisibleDevices()
if len(devices) == 0 {
return nil, "no devices requested"
}
// 检查驱动能力
// NVIDIA_DRIVER_CAPABILITIES 环境变量指定所需能力
// graphics: 包括 OpenGL、Vulkan 等
// display: 包括 X11 显示支持
if !cudaImage.GetDriverCapabilities().Any(image.DriverCapabilityGraphics, image.DriverCapabilityDisplay) {
return nil, "no required capabilities requested"
}
return devices, ""
}
3.8 gated.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/gated.go
// 职责: gatedModifier — 功能门控修改器
// 根据环境变量注入可选功能:GDS, MOFED, GDRCOPY, NVSWITCH, CUDA Compat
// =============================================================================
package modifier
import (
"fmt" // 格式化错误
"github.com/NVIDIA/nvidia-container-toolkit/api/config/v1"
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// newFeatureGatedModifier 创建功能门控修改器。
// 在 legacy 和 csv 模式下使用。
//
// 功能门控修改器包含两个部分:
// 1. 基于 gatedDevices 的自动 CDI spec 修改(GDS/MOFED/GDRCOPY/NVSWITCH)
// 2. CUDA Compat 模式 hook 注入
//
// 前提条件:
// – 必须有可见设备请求(否则不做任何修改)
//
// 返回:
// oci.SpecModifier – 功能门控修改器列表,或 nil
// error – 错误信息
func (f *Factory) newFeatureGatedModifier() (oci.SpecModifier, error) {
// 检查是否有可见设备
if devices := f.image.VisibleDevices(); len(devices) == 0 {
f.logger.Infof("No modification required; no devices requested")
return nil, nil
}
var modifers list // 修改器列表
// ========================================================================
// 部分1: Gated 设备(GDS/MOFED/GDRCOPY/NVSWITCH)
// ========================================================================
// gatedDevices 检查 NVIDIA_GDS, NVIDIA_MOFED, NVIDIA_GDRCOPY, NVIDIA_NVSWITCH 环境变量
// 如果任一设置为 "enabled",创建对应的自动 CDI spec 修改器
if gatedDeviceRequests := withUniqueDevices(gatedDevices(*f.image)).DeviceRequests(); len(gatedDeviceRequests) != 0 {
featureGatedModifier, err := f.newAutomaticCDISpecModifier(gatedDeviceRequests)
if err != nil {
return nil, err
}
modifers = append(modifers, featureGatedModifier)
}
// ========================================================================
// 部分2: CUDA Compat 模式 hook
// ========================================================================
// 如果未显式禁用 CUDA Compat hook 功能
if !f.cfg.Features.DisableCUDACompatLibHook.IsEnabled() {
cudaCompatModifer, err := f.getCudaCompatModeModifier()
if err != nil {
return nil, fmt.Errorf("failed to construct CUDA Compat discoverer: %w", err)
}
modifers = append(modifers, cudaCompatModifer)
}
return modifers, nil
}
// getCudaCompatModeModifier 创建 CUDA Compat 模式修改器。
//
// CUDA Compat 模式允许在安装了不同 CUDA 驱动版本的主机上运行容器。
//
// 模式判断:
// 1. CSV 模式 → 不支持,返回 nil
// 2. Legacy 模式 → 仅当 CUDACompatMode == "hook" 时启用
// 3. 其他模式 → 直接启用
//
// Legacy 模式额外处理:
// 除了 CUDA compat hook,还需要注入 update-ldcache hook
// (因为 legacy 模式没有自动的 ldconfig 更新)
//
// 返回:
// oci.SpecModifier – CUDA compat 修改器
// error – 错误信息
func (f *Factory) getCudaCompatModeModifier() (oci.SpecModifier, error) {
// CSV 模式不支持 CUDA compat hook
if f.cfg.NVIDIAContainerRuntimeConfig.Mode == "csv" {
return nil, nil
}
// Legacy 模式: 仅当配置为 hook 模式时启用
if f.cfg.NVIDIAContainerRuntimeConfig.Mode == "legacy" &&
f.cfg.NVIDIAContainerRuntimeConfig.Modes.Legacy.CUDACompatMode != config.CUDACompatModeHook {
return nil, nil
}
// 获取驱动版本(用于判断是否需要 CUDA compat)
version, err := f.driver.Version()
if err != nil {
return nil, fmt.Errorf("failed to get driver version: %w", err)
}
// 创建 CUDA compat hook 发现器
compatLibHookDiscoverer := discover.NewCUDACompatHookDiscoverer(
f.logger,
f.hookCreator,
&discover.EnableCUDACompatHookOptions{HostDriverVersion: version},
)
// 非 legacy 模式: 直接返回 hook 发现器对应的修改器
// 这些模式通常已有 update-ldcache hook
if f.cfg.NVIDIAContainerRuntimeConfig.Mode != "legacy" {
return f.newModifierFromDiscoverer(compatLibHookDiscoverer)
}
// Legacy 模式: 额外需要 update-ldcache hook
// 因为 legacy 模式没有自动的 ldconfig 更新机制
ldcacheUpdateHookDiscoverer, err := discover.NewLDCacheUpdateHook(
f.logger,
discover.None{}, // 空发现器作为基础
f.hookCreator,
)
if err != nil {
return nil, fmt.Errorf("failed to construct ldcache update discoverer: %w", err)
}
// 合并两个发现器
return f.newModifierFromDiscoverer(discover.Merge(compatLibHookDiscoverer, ldcacheUpdateHookDiscoverer))
}
3.9 list.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/list.go
// 职责: list — 修改器列表,实现 SpecModifier 接口
// 将多个修改器组合为一个,按顺序应用
// =============================================================================
package modifier
import (
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// list 是 SpecModifier 的列表类型
type list []oci.SpecModifier
// Modify 按顺序应用列表中的所有修改器。
// nil 修改器会被跳过。
// 如果任一修改器返回错误,立即停止并返回该错误。
//
// 这种设计允许:
// 1. 组合多个修改器为一个逻辑单元
// 2. 控制修改器的执行顺序
// 3. 添加/移除修改器而不影响其他修改器
//
// 参数:
// spec *specs.Spec – 要修改的 OCI spec
//
// 返回:
// error – 第一个遇到的错误,或 nil
func (m list) Modify(spec *specs.Spec) error {
for _, mm := range m {
// 跳过 nil 修改器
if mm == nil {
continue
}
// 应用修改器
err := mm.Modify(spec)
if err != nil {
// 遇到错误立即返回
return err
}
}
return nil
}
3.10 discover.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/discover.go
// 职责: discoverModifier — 基于发现器结果的修改器
// 将 discover.Discoverer 接口发现的结果应用到 OCI spec
// =============================================================================
package modifier
import (
"fmt" // 格式化错误
ocispecs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/NVIDIA/nvidia-container-toolkit/internal/discover"
"github.com/NVIDIA/nvidia-container-toolkit/internal/edits"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// discoverModifier 将 discover.Discoverer 的发现结果应用到 OCI spec。
//
// discover.Discoverer 接口提供:
// – Hooks() → 容器生命周期 hook 列表
// – Devices() → 设备节点列表
// – Mounts() → 挂载点列表
//
// discoverModifier 通过 editsFactory 将这些发现结果转换为 OCI spec 编辑
//
// 使用场景:
// – graphicsModifier: DRM 节点 + 图形库挂载
// – gatedModifier: CUDA compat hook
// – CSV 模式: CSV 文件定义的设备/挂载
type discoverModifier struct {
logger logger.Interface
editsFactory edits.Factory // edits 工厂,将发现结果转换为 spec 编辑
discoverer discover.Discover // 发现器
}
// newModifierFromDiscoverer 从发现器创建修改器
func (f *Factory) newModifierFromDiscoverer(d discover.Discover) (oci.SpecModifier, error) {
m := discoverModifier{
logger: f.logger,
editsFactory: f.editsFactory,
discoverer: d,
}
return &m, nil
}
// Modify 将发现器的结果应用到 OCI spec。
//
// 处理流程:
// 1. 通过 editsFactory 从发现器创建 spec 编辑
// 2. 记录所有挂载、设备、hook 的日志
// 3. 将编辑应用到 spec
//
// 参数:
// spec *ocispecs.Spec – 要修改的 OCI spec
//
// 返回:
// error – 错误信息
func (m discoverModifier) Modify(spec *ocispecs.Spec) error {
// 从发现器创建 spec 编辑
// editsFactory.FromDiscoverer 内部调用:
// discoverer.Hooks() → 转换为 hook 编辑
// discoverer.Devices() → 转换为设备编辑
// discoverer.Mounts() → 转换为挂载编辑
specEdits, err := m.editsFactory.FromDiscoverer(m.discoverer)
if err != nil {
return fmt.Errorf("failed to get required container edits: %v", err)
}
// 记录挂载信息
m.logger.Infof("Mounts:")
for _, mount := range specEdits.Mounts {
m.logger.Infof("Mounting %v at %v", mount.HostPath, mount.ContainerPath)
}
// 记录设备信息
m.logger.Infof("Devices:")
for _, device := range specEdits.DeviceNodes {
m.logger.Infof("Injecting %v", device.Path)
}
// 记录 hook 信息
m.logger.Infof("Hooks:")
for _, hook := range specEdits.Hooks {
m.logger.Infof("Injecting %v %v", hook.Path, hook.Args)
}
// 应用所有编辑到 spec
// specEdits.Apply 修改 spec:
// – 添加挂载点到 spec.Mounts
// – 添加设备到 spec.Linux.Devices
// – 添加 hook 到 spec.Hooks
// – 添加 cgroup 规则到 spec.Linux.Resources
return specEdits.Apply(spec)
}
3.11 cdi/builder.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/cdi/builder.go
// 职责: CDI 修改器的构建器 — 根据配置创建不同类型的 CDI 修改器
// =============================================================================
package cdi
import (
"fmt" // 格式化错误
"tags.cncf.io/container-device-interface/pkg/cdi"
"tags.cncf.io/container-device-interface/specs-go"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// builder 是 CDI 修改器的构建器
type builder struct {
logger logger.Interface // 日志记录器
specDirs []string // CDI spec 目录列表
devices []string // 要注入的设备列表
cdiSpec *specs.Spec // 原始 CDI spec(用于内存 spec 模式)
}
// Option 是 CDI 修改器构建器的配置函数
type Option func(*builder)
// New 创建 CDI 修改器。
// 这是 cdi 子包的入口函数。
//
// 两种模式:
// 1. 使用预生成的 CDI spec 文件(fromRegistry)
// – 需要设置 specDirs(CDI spec 目录)
// – 需要设置 devices(设备列表)
// – 从目录读取 .json/.yaml CDI spec 文件
//
// 2. 使用内存中的 CDI spec(fromCDISpec)
// – 需要设置 cdiSpec(原始 spec)
// – 直接应用 spec 中定义的设备编辑
//
// 参数:
// opts …Option – 配置选项
//
// 返回:
// oci.SpecModifier – CDI 修改器,或 nil(无设备且无 spec)
// error – 错误信息
func New(opts …Option) (oci.SpecModifier, error) {
b := &builder{}
for _, opt := range opts {
opt(b)
}
// 默认日志记录器
if b.logger == nil {
b.logger = logger.New()
}
return b.build()
}
// build 使用构建器配置构造 CDI 修改器
func (m builder) build() (oci.SpecModifier, error) {
// 如果没有设备请求且没有 spec → 返回 nil
if len(m.devices) == 0 && m.cdiSpec == nil {
return nil, nil
}
// 如果有原始 spec → 创建 fromCDISpec
if m.cdiSpec != nil {
modifier := fromCDISpec{
cdiSpec: &cdi.Spec{Spec: m.cdiSpec},
}
return modifier, nil
}
// 否则 → 创建 fromRegistry(从文件系统读取 spec)
registry, err := cdi.NewCache(
cdi.WithAutoRefresh(false), // 禁用自动刷新
cdi.WithSpecDirs(m.specDirs…), // 设置 spec 目录
)
if err != nil {
return nil, fmt.Errorf("failed to create CDI registry: %v", err)
}
modifier := fromRegistry{
logger: m.logger,
registry: registry,
devices: m.devices,
}
return modifier, nil
}
// =============================================================================
// Option 函数
// =============================================================================
func WithLogger(logger logger.Interface) Option {
return func(b *builder) {
b.logger = logger
}
}
func WithSpecDirs(specDirs …string) Option {
return func(b *builder) {
b.specDirs = specDirs
}
}
func WithDevices(devices …string) Option {
return func(b *builder) {
b.devices = devices
}
}
func WithSpec(spec *specs.Spec) Option {
return func(b *builder) {
b.cdiSpec = spec
}
}
3.12 cdi/registry.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/cdi/registry.go
// 职责: fromRegistry — 基于 CDI 注册表(Cache)的修改器
// 从 CDI spec 目录读取 spec,注入设备到 OCI spec
// =============================================================================
package cdi
import (
"errors" // 错误合并
"fmt" // 格式化错误
"github.com/opencontainers/runtime-spec/specs-go"
"tags.cncf.io/container-device-interface/pkg/cdi"
"github.com/NVIDIA/nvidia-container-toolkit/internal/logger"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// fromRegistry 使用 CDI Cache(注册表)来注入设备。
// CDI Cache 从指定目录读取 CDI spec 文件(JSON 或 YAML)。
//
// 工作流程:
// 1. Refresh() → 从目录加载/刷新 CDI spec
// 2. InjectDevices(spec, devices) → 将设备注入到 OCI spec
//
// CDI spec 文件通常位于:
// /var/run/cdi/ → 运行时生成的 spec
// /etc/cdi/ → 静态 spec
type fromRegistry struct {
logger logger.Interface
registry *cdi.Cache // CDI 缓存
devices []string // 要注入的设备列表
}
var _ oci.SpecModifier = (*fromRegistry)(nil)
// Modify 将 CDI 设备注入到 OCI spec。
//
// 处理流程:
// 1. 刷新 CDI 注册表(从目录重新加载 spec)
// 2. 调用 InjectDevices 注入设备
// 3. 处理未解析的设备
// 4. 收集并报告刷新错误
//
// 参数:
// spec *specs.Spec – 要修改的 OCI spec
//
// 返回:
// error – 注入失败
func (m fromRegistry) Modify(spec *specs.Spec) error {
// 刷新 CDI 注册表
// 从 specDirs 加载所有 CDI spec 文件
if err := m.registry.Refresh(); err != nil {
m.logger.Debugf("The following error was triggered when refreshing the CDI registry: %v", err)
// 刷新错误不是致命的,继续尝试注入
}
// 注入设备到 spec
m.logger.Debugf("Injecting devices using CDI: %v", m.devices)
unresolvedDevices, err := m.registry.InjectDevices(spec, m.devices…)
if unresolvedDevices != nil {
m.logger.Warningf("could not resolve CDI devices: %v", unresolvedDevices)
}
if err != nil {
// 收集刷新过程中产生的所有错误
var refreshErrors []error
for _, rerrs := range m.registry.GetErrors() {
refreshErrors = append(refreshErrors, rerrs…)
}
if rerr := errors.Join(refreshErrors…); rerr != nil {
m.logger.Warningf("Refreshing the CDI registry generated errors: %v", rerr)
}
return fmt.Errorf("failed to inject CDI devices: %v", err)
}
return nil
}
3.13 cdi/spec.go 完整代码逐行注释
// =============================================================================
// 文件: internal/modifier/cdi/spec.go
// 职责: fromCDISpec — 基于原始 CDI spec 的修改器
// 直接应用内存中的 CDI spec,不从文件系统读取
// =============================================================================
package cdi
import (
"fmt" // 格式化错误
"github.com/opencontainers/runtime-spec/specs-go"
cdiapi "tags.cncf.io/container-device-interface/pkg/cdi"
cdi "tags.cncf.io/container-device-interface/specs-go"
"github.com/NVIDIA/nvidia-container-toolkit/internal/devices"
"github.com/NVIDIA/nvidia-container-toolkit/internal/oci"
)
// fromCDISpec 使用内存中的 CDI spec 来修改 OCI spec。
// 与 fromRegistry 不同,它不从文件系统读取,直接应用给定的 spec。
//
// 使用场景:
// – JIT-CDI 模式: nvcdi 在运行时生成 spec
// – CSV 模式: 从 CSV 文件生成 spec
// – 自动设备模式: 为自动设备生成 spec
type fromCDISpec struct {
cdiSpec *cdiapi.Spec // CDI spec(包含设备定义和编辑)
}
var _ oci.SpecModifier = (*fromCDISpec)(nil)
// Modify 将 CDI spec 中定义的设备编辑应用到 OCI spec。
//
// 处理流程:
// 1. 遍历 CDI spec 中的所有设备
// 2. 对每个设备:
// a. 调用 enrichDevice 补充设备节点信息(测试覆盖用)
// b. 调用 ApplyEdits 将设备编辑应用到 spec
// 3. 最后应用 spec 级别的全局编辑
//
// CDI 设备编辑包括:
// – DeviceNodes: 设备节点(/dev/nvidia0 等)
// – Mounts: 挂载点(库文件等)
// – Hooks: 生命周期 hook
// – Env: 环境变量
//
// 参数:
// spec *specs.Spec – 要修改的 OCI spec
//
// 返回:
// error – 应用编辑失败
func (m fromCDISpec) Modify(spec *specs.Spec) error {
for _, device := range m.cdiSpec.Devices {
// enrichDevice 补充设备节点信息(主要用于测试)
device := m.enrichDevice(device)
// 创建 CDI Device 包装器
cdiDevice := cdiapi.Device{
Device: &device,
}
// 应用设备编辑到 spec
// ApplyEdits 将 device.ContainerEdits 应用到 OCI spec:
// – 添加设备节点到 spec.Linux.Devices
// – 添加挂载到 spec.Mounts
// – 添加 hook 到 spec.Hooks
// – 添加环境变量到 spec.Process.Env
if err := cdiDevice.ApplyEdits(spec); err != nil {
return fmt.Errorf("failed to apply edits for device %q: %v", m.cdiSpec.Kind+"="+device.Name, err)
}
}
// 应用 spec 级别的全局编辑
// 这些编辑不与特定设备关联,应用于所有容器
return m.cdiSpec.ApplyEdits(spec)
}
// enrichDevice 补充设备节点信息。
// 主要用于测试场景:当设备覆盖被激活时,为缺少信息的设备节点添加默认值。
//
// 处理逻辑:
// 1. 如果设备覆盖未激活 → 直接返回原设备
// 2. 遍历设备的 DeviceNodes:
// a. 设置 Type 为 "c"(字符设备)
// b. 如果 Major 为 0,设置为 99(测试用默认值)
//
// 这些默认值确保测试中不需要真实的设备节点
func (m fromCDISpec) enrichDevice(device cdi.Device) cdi.Device {
if !devices.IsOverrideApplied() {
return device
}
for i, dn := range device.ContainerEdits.DeviceNodes {
dn.Type = "c"
if dn.Major == 0 {
dn.Major = 99
}
device.ContainerEdits.DeviceNodes[i] = dn
}
return device
}
四、Mermaid 图表
图1: Modifier 模块类图
#mermaid-svg-eSEtHGMXUMnr8j4l{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-eSEtHGMXUMnr8j4l .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eSEtHGMXUMnr8j4l .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eSEtHGMXUMnr8j4l .error-icon{fill:#552222;}#mermaid-svg-eSEtHGMXUMnr8j4l .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eSEtHGMXUMnr8j4l .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eSEtHGMXUMnr8j4l .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eSEtHGMXUMnr8j4l .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eSEtHGMXUMnr8j4l .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eSEtHGMXUMnr8j4l .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eSEtHGMXUMnr8j4l .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eSEtHGMXUMnr8j4l .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eSEtHGMXUMnr8j4l .marker.cross{stroke:#333333;}#mermaid-svg-eSEtHGMXUMnr8j4l svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eSEtHGMXUMnr8j4l p{margin:0;}#mermaid-svg-eSEtHGMXUMnr8j4l g.classGroup text{fill:#9370DB;stroke:none;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-eSEtHGMXUMnr8j4l g.classGroup text .title{font-weight:bolder;}#mermaid-svg-eSEtHGMXUMnr8j4l .cluster-label text{fill:#333;}#mermaid-svg-eSEtHGMXUMnr8j4l .cluster-label span{color:#333;}#mermaid-svg-eSEtHGMXUMnr8j4l .cluster-label span p{background-color:transparent;}#mermaid-svg-eSEtHGMXUMnr8j4l .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eSEtHGMXUMnr8j4l .cluster text{fill:#333;}#mermaid-svg-eSEtHGMXUMnr8j4l .cluster span{color:#333;}#mermaid-svg-eSEtHGMXUMnr8j4l .nodeLabel,#mermaid-svg-eSEtHGMXUMnr8j4l .edgeLabel{color:#131300;}#mermaid-svg-eSEtHGMXUMnr8j4l .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-eSEtHGMXUMnr8j4l .label text{fill:#131300;}#mermaid-svg-eSEtHGMXUMnr8j4l .labelBkg{background:#ECECFF;}#mermaid-svg-eSEtHGMXUMnr8j4l .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-eSEtHGMXUMnr8j4l .classTitle{font-weight:bolder;}#mermaid-svg-eSEtHGMXUMnr8j4l .node rect,#mermaid-svg-eSEtHGMXUMnr8j4l .node circle,#mermaid-svg-eSEtHGMXUMnr8j4l .node ellipse,#mermaid-svg-eSEtHGMXUMnr8j4l .node polygon,#mermaid-svg-eSEtHGMXUMnr8j4l .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eSEtHGMXUMnr8j4l .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l g.clickable{cursor:pointer;}#mermaid-svg-eSEtHGMXUMnr8j4l g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-eSEtHGMXUMnr8j4l g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-eSEtHGMXUMnr8j4l .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-eSEtHGMXUMnr8j4l .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-eSEtHGMXUMnr8j4l .dashed-line{stroke-dasharray:3;}#mermaid-svg-eSEtHGMXUMnr8j4l .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-eSEtHGMXUMnr8j4l #compositionStart,#mermaid-svg-eSEtHGMXUMnr8j4l .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l #compositionEnd,#mermaid-svg-eSEtHGMXUMnr8j4l .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l #dependencyStart,#mermaid-svg-eSEtHGMXUMnr8j4l .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l #dependencyStart,#mermaid-svg-eSEtHGMXUMnr8j4l .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l #extensionStart,#mermaid-svg-eSEtHGMXUMnr8j4l .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l #extensionEnd,#mermaid-svg-eSEtHGMXUMnr8j4l .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l #aggregationStart,#mermaid-svg-eSEtHGMXUMnr8j4l .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l #aggregationEnd,#mermaid-svg-eSEtHGMXUMnr8j4l .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l #lollipopStart,#mermaid-svg-eSEtHGMXUMnr8j4l .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l #lollipopEnd,#mermaid-svg-eSEtHGMXUMnr8j4l .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-eSEtHGMXUMnr8j4l .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-eSEtHGMXUMnr8j4l .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eSEtHGMXUMnr8j4l .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eSEtHGMXUMnr8j4l .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eSEtHGMXUMnr8j4l :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
实现
实现
实现
实现
实现
实现
实现
创建
创建
创建
创建
创建
包含
包含
包含
«interface»
SpecModifier
+Modify(*specs.Spec) : error
Factory
-logger logger.Interface
-cfg *config.Config
-driver *root.Driver
-hookCreator HookCreator
-image *image.CUDA
-runtimeMode RuntimeMode
-editsFactory edits.Factory
+New(opts)(SpecModifier, error)
+create()(SpecModifier, error)
+Modify(*Spec) : error
-validate() : error
«type []SpecModifier»
list
+Modify(*Spec) : error
stableRuntimeModifier
-logger logger.Interface
-nvidiaContainerRuntimeHookPath string
+Modify(*Spec) : error
csvModifier
+Modify(*Spec) : error
cdiModifier
+Modify(*Spec) : error
nvidiaContainerRuntimeHookRemover
-logger logger.Interface
+Modify(*Spec) : error
graphicsModifier
+Modify(*Spec) : error
discoverModifier
-logger logger.Interface
-editsFactory edits.Factory
-discoverer discover.Discover
+Modify(*Spec) : error
cdi_fromRegistry
-logger logger.Interface
-registry *cdi.Cache
-devices []string
+Modify(*Spec) : error
cdi_fromCDISpec
-cdiSpec *cdi.Spec
+Modify(*Spec) : error
-enrichDevice(device) : device
图2: Factory.create() 流程图
#mermaid-svg-iJNGidgxnayOXNVI{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-iJNGidgxnayOXNVI .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-iJNGidgxnayOXNVI .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-iJNGidgxnayOXNVI .error-icon{fill:#552222;}#mermaid-svg-iJNGidgxnayOXNVI .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-iJNGidgxnayOXNVI .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-iJNGidgxnayOXNVI .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-iJNGidgxnayOXNVI .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-iJNGidgxnayOXNVI .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-iJNGidgxnayOXNVI .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-iJNGidgxnayOXNVI .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-iJNGidgxnayOXNVI .marker{fill:#333333;stroke:#333333;}#mermaid-svg-iJNGidgxnayOXNVI .marker.cross{stroke:#333333;}#mermaid-svg-iJNGidgxnayOXNVI svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-iJNGidgxnayOXNVI p{margin:0;}#mermaid-svg-iJNGidgxnayOXNVI .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-iJNGidgxnayOXNVI .cluster-label text{fill:#333;}#mermaid-svg-iJNGidgxnayOXNVI .cluster-label span{color:#333;}#mermaid-svg-iJNGidgxnayOXNVI .cluster-label span p{background-color:transparent;}#mermaid-svg-iJNGidgxnayOXNVI .label text,#mermaid-svg-iJNGidgxnayOXNVI span{fill:#333;color:#333;}#mermaid-svg-iJNGidgxnayOXNVI .node rect,#mermaid-svg-iJNGidgxnayOXNVI .node circle,#mermaid-svg-iJNGidgxnayOXNVI .node ellipse,#mermaid-svg-iJNGidgxnayOXNVI .node polygon,#mermaid-svg-iJNGidgxnayOXNVI .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-iJNGidgxnayOXNVI .rough-node .label text,#mermaid-svg-iJNGidgxnayOXNVI .node .label text,#mermaid-svg-iJNGidgxnayOXNVI .image-shape .label,#mermaid-svg-iJNGidgxnayOXNVI .icon-shape .label{text-anchor:middle;}#mermaid-svg-iJNGidgxnayOXNVI .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-iJNGidgxnayOXNVI .rough-node .label,#mermaid-svg-iJNGidgxnayOXNVI .node .label,#mermaid-svg-iJNGidgxnayOXNVI .image-shape .label,#mermaid-svg-iJNGidgxnayOXNVI .icon-shape .label{text-align:center;}#mermaid-svg-iJNGidgxnayOXNVI .node.clickable{cursor:pointer;}#mermaid-svg-iJNGidgxnayOXNVI .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-iJNGidgxnayOXNVI .arrowheadPath{fill:#333333;}#mermaid-svg-iJNGidgxnayOXNVI .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-iJNGidgxnayOXNVI .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-iJNGidgxnayOXNVI .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iJNGidgxnayOXNVI .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-iJNGidgxnayOXNVI .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iJNGidgxnayOXNVI .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-iJNGidgxnayOXNVI .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-iJNGidgxnayOXNVI .cluster text{fill:#333;}#mermaid-svg-iJNGidgxnayOXNVI .cluster span{color:#333;}#mermaid-svg-iJNGidgxnayOXNVI 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-iJNGidgxnayOXNVI .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-iJNGidgxnayOXNVI rect.text{fill:none;stroke-width:0;}#mermaid-svg-iJNGidgxnayOXNVI .icon-shape,#mermaid-svg-iJNGidgxnayOXNVI .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-iJNGidgxnayOXNVI .icon-shape p,#mermaid-svg-iJNGidgxnayOXNVI .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-iJNGidgxnayOXNVI .icon-shape .label rect,#mermaid-svg-iJNGidgxnayOXNVI .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-iJNGidgxnayOXNVI .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-iJNGidgxnayOXNVI .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-iJNGidgxnayOXNVI :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
legacy
csv
cdi/jit-cdi
mode
nvidia-hook-remover
graphics
feature-gated
未知
legacy
csv
cdi
jit-cdi
Factory.create()
supportedModifierTypes(runtimeMode)
模式?
['feature-gated', 'graphics', 'mode']
['nvidia-hook-remover', 'feature-gated', 'mode']
['nvidia-hook-remover', 'mode']
遍历类型列表
类型?
newModeModifier()
newNvidiaContainerRuntimeHookRemover()
newGraphicsModifier()
newFeatureGatedModifier()
Debug: 忽略
模式?
newStableRuntimeModifier()
newCSVModifier()
newCDIModifier(false)
newCDIModifier(true)
添加到 modifiers list
返回 list
图3: supportedModifierTypes() 决策矩阵图
#mermaid-svg-E0eioTihf2Mcn5Cu{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-E0eioTihf2Mcn5Cu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-E0eioTihf2Mcn5Cu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-E0eioTihf2Mcn5Cu .error-icon{fill:#552222;}#mermaid-svg-E0eioTihf2Mcn5Cu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-E0eioTihf2Mcn5Cu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-E0eioTihf2Mcn5Cu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-E0eioTihf2Mcn5Cu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-E0eioTihf2Mcn5Cu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-E0eioTihf2Mcn5Cu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-E0eioTihf2Mcn5Cu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-E0eioTihf2Mcn5Cu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-E0eioTihf2Mcn5Cu .marker.cross{stroke:#333333;}#mermaid-svg-E0eioTihf2Mcn5Cu svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-E0eioTihf2Mcn5Cu p{margin:0;}#mermaid-svg-E0eioTihf2Mcn5Cu .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-E0eioTihf2Mcn5Cu .cluster-label text{fill:#333;}#mermaid-svg-E0eioTihf2Mcn5Cu .cluster-label span{color:#333;}#mermaid-svg-E0eioTihf2Mcn5Cu .cluster-label span p{background-color:transparent;}#mermaid-svg-E0eioTihf2Mcn5Cu .label text,#mermaid-svg-E0eioTihf2Mcn5Cu span{fill:#333;color:#333;}#mermaid-svg-E0eioTihf2Mcn5Cu .node rect,#mermaid-svg-E0eioTihf2Mcn5Cu .node circle,#mermaid-svg-E0eioTihf2Mcn5Cu .node ellipse,#mermaid-svg-E0eioTihf2Mcn5Cu .node polygon,#mermaid-svg-E0eioTihf2Mcn5Cu .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-E0eioTihf2Mcn5Cu .rough-node .label text,#mermaid-svg-E0eioTihf2Mcn5Cu .node .label text,#mermaid-svg-E0eioTihf2Mcn5Cu .image-shape .label,#mermaid-svg-E0eioTihf2Mcn5Cu .icon-shape .label{text-anchor:middle;}#mermaid-svg-E0eioTihf2Mcn5Cu .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-E0eioTihf2Mcn5Cu .rough-node .label,#mermaid-svg-E0eioTihf2Mcn5Cu .node .label,#mermaid-svg-E0eioTihf2Mcn5Cu .image-shape .label,#mermaid-svg-E0eioTihf2Mcn5Cu .icon-shape .label{text-align:center;}#mermaid-svg-E0eioTihf2Mcn5Cu .node.clickable{cursor:pointer;}#mermaid-svg-E0eioTihf2Mcn5Cu .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-E0eioTihf2Mcn5Cu .arrowheadPath{fill:#333333;}#mermaid-svg-E0eioTihf2Mcn5Cu .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-E0eioTihf2Mcn5Cu .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-E0eioTihf2Mcn5Cu .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E0eioTihf2Mcn5Cu .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-E0eioTihf2Mcn5Cu .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E0eioTihf2Mcn5Cu .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-E0eioTihf2Mcn5Cu .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-E0eioTihf2Mcn5Cu .cluster text{fill:#333;}#mermaid-svg-E0eioTihf2Mcn5Cu .cluster span{color:#333;}#mermaid-svg-E0eioTihf2Mcn5Cu 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-E0eioTihf2Mcn5Cu .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-E0eioTihf2Mcn5Cu rect.text{fill:none;stroke-width:0;}#mermaid-svg-E0eioTihf2Mcn5Cu .icon-shape,#mermaid-svg-E0eioTihf2Mcn5Cu .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-E0eioTihf2Mcn5Cu .icon-shape p,#mermaid-svg-E0eioTihf2Mcn5Cu .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-E0eioTihf2Mcn5Cu .icon-shape .label rect,#mermaid-svg-E0eioTihf2Mcn5Cu .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-E0eioTihf2Mcn5Cu .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-E0eioTihf2Mcn5Cu .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-E0eioTihf2Mcn5Cu :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
jit-cdi 模式
nvidia-hook-remover
mode(jit-cdi)
cdi 模式
nvidia-hook-remover
mode(cdi)
csv 模式
nvidia-hook-remover
feature-gated
mode(csv)
legacy 模式
feature-gated
graphics
mode(stable)
设计原因: legacy: 不需要移除hook (自己注入hook) 需要graphics和feature-gated 因为hook模式不自动处理
设计原因: csv: 移除旧hook feature-gated用于GDS等 不需要graphics (CSV文件定义图形挂载)
设计原因: cdi/jit-cdi: 移除旧hook CDI spec已包含所有修改 不需要额外的graphics/feature-gated
图4: stableRuntimeModifier.Modify() 流程图
#mermaid-svg-4lIS624BC1pvMx7f{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-4lIS624BC1pvMx7f .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4lIS624BC1pvMx7f .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4lIS624BC1pvMx7f .error-icon{fill:#552222;}#mermaid-svg-4lIS624BC1pvMx7f .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4lIS624BC1pvMx7f .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4lIS624BC1pvMx7f .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4lIS624BC1pvMx7f .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4lIS624BC1pvMx7f .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4lIS624BC1pvMx7f .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4lIS624BC1pvMx7f .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4lIS624BC1pvMx7f .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4lIS624BC1pvMx7f .marker.cross{stroke:#333333;}#mermaid-svg-4lIS624BC1pvMx7f svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4lIS624BC1pvMx7f p{margin:0;}#mermaid-svg-4lIS624BC1pvMx7f .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-4lIS624BC1pvMx7f .cluster-label text{fill:#333;}#mermaid-svg-4lIS624BC1pvMx7f .cluster-label span{color:#333;}#mermaid-svg-4lIS624BC1pvMx7f .cluster-label span p{background-color:transparent;}#mermaid-svg-4lIS624BC1pvMx7f .label text,#mermaid-svg-4lIS624BC1pvMx7f span{fill:#333;color:#333;}#mermaid-svg-4lIS624BC1pvMx7f .node rect,#mermaid-svg-4lIS624BC1pvMx7f .node circle,#mermaid-svg-4lIS624BC1pvMx7f .node ellipse,#mermaid-svg-4lIS624BC1pvMx7f .node polygon,#mermaid-svg-4lIS624BC1pvMx7f .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4lIS624BC1pvMx7f .rough-node .label text,#mermaid-svg-4lIS624BC1pvMx7f .node .label text,#mermaid-svg-4lIS624BC1pvMx7f .image-shape .label,#mermaid-svg-4lIS624BC1pvMx7f .icon-shape .label{text-anchor:middle;}#mermaid-svg-4lIS624BC1pvMx7f .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4lIS624BC1pvMx7f .rough-node .label,#mermaid-svg-4lIS624BC1pvMx7f .node .label,#mermaid-svg-4lIS624BC1pvMx7f .image-shape .label,#mermaid-svg-4lIS624BC1pvMx7f .icon-shape .label{text-align:center;}#mermaid-svg-4lIS624BC1pvMx7f .node.clickable{cursor:pointer;}#mermaid-svg-4lIS624BC1pvMx7f .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4lIS624BC1pvMx7f .arrowheadPath{fill:#333333;}#mermaid-svg-4lIS624BC1pvMx7f .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4lIS624BC1pvMx7f .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4lIS624BC1pvMx7f .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4lIS624BC1pvMx7f .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4lIS624BC1pvMx7f .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4lIS624BC1pvMx7f .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4lIS624BC1pvMx7f .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4lIS624BC1pvMx7f .cluster text{fill:#333;}#mermaid-svg-4lIS624BC1pvMx7f .cluster span{color:#333;}#mermaid-svg-4lIS624BC1pvMx7f 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-4lIS624BC1pvMx7f .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4lIS624BC1pvMx7f rect.text{fill:none;stroke-width:0;}#mermaid-svg-4lIS624BC1pvMx7f .icon-shape,#mermaid-svg-4lIS624BC1pvMx7f .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4lIS624BC1pvMx7f .icon-shape p,#mermaid-svg-4lIS624BC1pvMx7f .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4lIS624BC1pvMx7f .icon-shape .label rect,#mermaid-svg-4lIS624BC1pvMx7f .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4lIS624BC1pvMx7f .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4lIS624BC1pvMx7f .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4lIS624BC1pvMx7f :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
是
否
是
是
否
stableRuntimeModifier.Modify(spec)
spec.Hooks != nil?
遍历 spec.Hooks.Prestart
遍历完成?
isNVIDIAContainerRuntimeHook?
Info: 已有 NVIDIA hook返回 nil (幂等)
获取 hook 路径
构造 args:[filepath.Base(path)]
spec.Hooks == nil?
创建 spec.Hooks = &Hooks{}
追加 prestart hook
spec.Hooks.Prestart = append( Prestart, Hook{Path: path, Args: [hook, 'prestart']})
返回 nil
图5: csvModifier 流程图
#mermaid-svg-QHTUkojcw64doQoW{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-QHTUkojcw64doQoW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-QHTUkojcw64doQoW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-QHTUkojcw64doQoW .error-icon{fill:#552222;}#mermaid-svg-QHTUkojcw64doQoW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-QHTUkojcw64doQoW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-QHTUkojcw64doQoW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-QHTUkojcw64doQoW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-QHTUkojcw64doQoW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-QHTUkojcw64doQoW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-QHTUkojcw64doQoW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-QHTUkojcw64doQoW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-QHTUkojcw64doQoW .marker.cross{stroke:#333333;}#mermaid-svg-QHTUkojcw64doQoW svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-QHTUkojcw64doQoW p{margin:0;}#mermaid-svg-QHTUkojcw64doQoW .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-QHTUkojcw64doQoW .cluster-label text{fill:#333;}#mermaid-svg-QHTUkojcw64doQoW .cluster-label span{color:#333;}#mermaid-svg-QHTUkojcw64doQoW .cluster-label span p{background-color:transparent;}#mermaid-svg-QHTUkojcw64doQoW .label text,#mermaid-svg-QHTUkojcw64doQoW span{fill:#333;color:#333;}#mermaid-svg-QHTUkojcw64doQoW .node rect,#mermaid-svg-QHTUkojcw64doQoW .node circle,#mermaid-svg-QHTUkojcw64doQoW .node ellipse,#mermaid-svg-QHTUkojcw64doQoW .node polygon,#mermaid-svg-QHTUkojcw64doQoW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-QHTUkojcw64doQoW .rough-node .label text,#mermaid-svg-QHTUkojcw64doQoW .node .label text,#mermaid-svg-QHTUkojcw64doQoW .image-shape .label,#mermaid-svg-QHTUkojcw64doQoW .icon-shape .label{text-anchor:middle;}#mermaid-svg-QHTUkojcw64doQoW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-QHTUkojcw64doQoW .rough-node .label,#mermaid-svg-QHTUkojcw64doQoW .node .label,#mermaid-svg-QHTUkojcw64doQoW .image-shape .label,#mermaid-svg-QHTUkojcw64doQoW .icon-shape .label{text-align:center;}#mermaid-svg-QHTUkojcw64doQoW .node.clickable{cursor:pointer;}#mermaid-svg-QHTUkojcw64doQoW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-QHTUkojcw64doQoW .arrowheadPath{fill:#333333;}#mermaid-svg-QHTUkojcw64doQoW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-QHTUkojcw64doQoW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-QHTUkojcw64doQoW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QHTUkojcw64doQoW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-QHTUkojcw64doQoW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QHTUkojcw64doQoW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-QHTUkojcw64doQoW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-QHTUkojcw64doQoW .cluster text{fill:#333;}#mermaid-svg-QHTUkojcw64doQoW .cluster span{color:#333;}#mermaid-svg-QHTUkojcw64doQoW 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-QHTUkojcw64doQoW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-QHTUkojcw64doQoW rect.text{fill:none;stroke-width:0;}#mermaid-svg-QHTUkojcw64doQoW .icon-shape,#mermaid-svg-QHTUkojcw64doQoW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-QHTUkojcw64doQoW .icon-shape p,#mermaid-svg-QHTUkojcw64doQoW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-QHTUkojcw64doQoW .icon-shape .label rect,#mermaid-svg-QHTUkojcw64doQoW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-QHTUkojcw64doQoW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-QHTUkojcw64doQoW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-QHTUkojcw64doQoW :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
是
否
否
是
newCSVModifier()
csvDevices(*f.image)提取可见设备
withUniqueDevices()去重
.DeviceRequests()转换为 mode=csv,id=XXX
设备列表为空?
返回 nil, nil(无需修改)
checkRequirements()
image.HasDisableRequire?
跳过需求检查
image.GetRequirements()
requirements.New(logger, reqs)
cuda.Version()获取主机CUDA版本
cuda.ComputeCapability(0)获取计算能力
r.Assert()断言需求满足
满足?
返回错误:requirements not met
newAutomaticCDISpecModifier(devices)
获取CSV文件列表
cdiModeIdentfiersFromDevices()提取模式标识符
对每个模式创建nvcdi实例
cdilib.GetSpec(ids)生成CDI spec
cdi.New(WithSpec(spec.Raw()))创建CDI修改器
返回 SpecModifiers 列表
图6: cdiModifier 流程图
#mermaid-svg-2fqt636NQ9QKYbP4{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-2fqt636NQ9QKYbP4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2fqt636NQ9QKYbP4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2fqt636NQ9QKYbP4 .error-icon{fill:#552222;}#mermaid-svg-2fqt636NQ9QKYbP4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2fqt636NQ9QKYbP4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2fqt636NQ9QKYbP4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2fqt636NQ9QKYbP4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2fqt636NQ9QKYbP4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2fqt636NQ9QKYbP4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2fqt636NQ9QKYbP4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2fqt636NQ9QKYbP4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2fqt636NQ9QKYbP4 .marker.cross{stroke:#333333;}#mermaid-svg-2fqt636NQ9QKYbP4 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2fqt636NQ9QKYbP4 p{margin:0;}#mermaid-svg-2fqt636NQ9QKYbP4 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-2fqt636NQ9QKYbP4 .cluster-label text{fill:#333;}#mermaid-svg-2fqt636NQ9QKYbP4 .cluster-label span{color:#333;}#mermaid-svg-2fqt636NQ9QKYbP4 .cluster-label span p{background-color:transparent;}#mermaid-svg-2fqt636NQ9QKYbP4 .label text,#mermaid-svg-2fqt636NQ9QKYbP4 span{fill:#333;color:#333;}#mermaid-svg-2fqt636NQ9QKYbP4 .node rect,#mermaid-svg-2fqt636NQ9QKYbP4 .node circle,#mermaid-svg-2fqt636NQ9QKYbP4 .node ellipse,#mermaid-svg-2fqt636NQ9QKYbP4 .node polygon,#mermaid-svg-2fqt636NQ9QKYbP4 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2fqt636NQ9QKYbP4 .rough-node .label text,#mermaid-svg-2fqt636NQ9QKYbP4 .node .label text,#mermaid-svg-2fqt636NQ9QKYbP4 .image-shape .label,#mermaid-svg-2fqt636NQ9QKYbP4 .icon-shape .label{text-anchor:middle;}#mermaid-svg-2fqt636NQ9QKYbP4 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2fqt636NQ9QKYbP4 .rough-node .label,#mermaid-svg-2fqt636NQ9QKYbP4 .node .label,#mermaid-svg-2fqt636NQ9QKYbP4 .image-shape .label,#mermaid-svg-2fqt636NQ9QKYbP4 .icon-shape .label{text-align:center;}#mermaid-svg-2fqt636NQ9QKYbP4 .node.clickable{cursor:pointer;}#mermaid-svg-2fqt636NQ9QKYbP4 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2fqt636NQ9QKYbP4 .arrowheadPath{fill:#333333;}#mermaid-svg-2fqt636NQ9QKYbP4 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2fqt636NQ9QKYbP4 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2fqt636NQ9QKYbP4 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2fqt636NQ9QKYbP4 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2fqt636NQ9QKYbP4 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2fqt636NQ9QKYbP4 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2fqt636NQ9QKYbP4 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2fqt636NQ9QKYbP4 .cluster text{fill:#333;}#mermaid-svg-2fqt636NQ9QKYbP4 .cluster span{color:#333;}#mermaid-svg-2fqt636NQ9QKYbP4 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-2fqt636NQ9QKYbP4 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2fqt636NQ9QKYbP4 rect.text{fill:none;stroke-width:0;}#mermaid-svg-2fqt636NQ9QKYbP4 .icon-shape,#mermaid-svg-2fqt636NQ9QKYbP4 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2fqt636NQ9QKYbP4 .icon-shape p,#mermaid-svg-2fqt636NQ9QKYbP4 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2fqt636NQ9QKYbP4 .icon-shape .label rect,#mermaid-svg-2fqt636NQ9QKYbP4 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2fqt636NQ9QKYbP4 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2fqt636NQ9QKYbP4 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2fqt636NQ9QKYbP4 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
是
否
是(全部以runtime.nvidia.com开头)
否(全是普通CDI设备)
混合
newCDIModifier(isJitCDI)
isJitCDI?
defaultKind = automaticDeviceKind(runtime.nvidia.com/gpu)
defaultKind = cfg.DefaultKind
newCDIDeviceRequestor()
withUniqueDevices()去重包装
.DeviceRequests()获取设备列表
设备列表为空?
返回 nil, nil
filterAutomaticDevices(devices)
全是自动设备?
newJitCDIModifier()
cdi.New()从specDirs读取
返回错误:不支持混合请求
附加gatedDevices(GDS/MOFED/GDRCOPY/NVSWITCH)
附加imexDevices(IMEX channel)
newAutomaticCDISpecModifier()
创建cdi.Cache从specDirs加载
fromRegistry注入设备
返回修改器
图7: jitCDIModifier 流程图
#mermaid-svg-y6GjKwSZRB3ROKQU{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-y6GjKwSZRB3ROKQU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y6GjKwSZRB3ROKQU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y6GjKwSZRB3ROKQU .error-icon{fill:#552222;}#mermaid-svg-y6GjKwSZRB3ROKQU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y6GjKwSZRB3ROKQU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y6GjKwSZRB3ROKQU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y6GjKwSZRB3ROKQU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y6GjKwSZRB3ROKQU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y6GjKwSZRB3ROKQU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y6GjKwSZRB3ROKQU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y6GjKwSZRB3ROKQU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y6GjKwSZRB3ROKQU .marker.cross{stroke:#333333;}#mermaid-svg-y6GjKwSZRB3ROKQU svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y6GjKwSZRB3ROKQU p{margin:0;}#mermaid-svg-y6GjKwSZRB3ROKQU .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-y6GjKwSZRB3ROKQU .cluster-label text{fill:#333;}#mermaid-svg-y6GjKwSZRB3ROKQU .cluster-label span{color:#333;}#mermaid-svg-y6GjKwSZRB3ROKQU .cluster-label span p{background-color:transparent;}#mermaid-svg-y6GjKwSZRB3ROKQU .label text,#mermaid-svg-y6GjKwSZRB3ROKQU span{fill:#333;color:#333;}#mermaid-svg-y6GjKwSZRB3ROKQU .node rect,#mermaid-svg-y6GjKwSZRB3ROKQU .node circle,#mermaid-svg-y6GjKwSZRB3ROKQU .node ellipse,#mermaid-svg-y6GjKwSZRB3ROKQU .node polygon,#mermaid-svg-y6GjKwSZRB3ROKQU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y6GjKwSZRB3ROKQU .rough-node .label text,#mermaid-svg-y6GjKwSZRB3ROKQU .node .label text,#mermaid-svg-y6GjKwSZRB3ROKQU .image-shape .label,#mermaid-svg-y6GjKwSZRB3ROKQU .icon-shape .label{text-anchor:middle;}#mermaid-svg-y6GjKwSZRB3ROKQU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-y6GjKwSZRB3ROKQU .rough-node .label,#mermaid-svg-y6GjKwSZRB3ROKQU .node .label,#mermaid-svg-y6GjKwSZRB3ROKQU .image-shape .label,#mermaid-svg-y6GjKwSZRB3ROKQU .icon-shape .label{text-align:center;}#mermaid-svg-y6GjKwSZRB3ROKQU .node.clickable{cursor:pointer;}#mermaid-svg-y6GjKwSZRB3ROKQU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-y6GjKwSZRB3ROKQU .arrowheadPath{fill:#333333;}#mermaid-svg-y6GjKwSZRB3ROKQU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-y6GjKwSZRB3ROKQU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-y6GjKwSZRB3ROKQU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y6GjKwSZRB3ROKQU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-y6GjKwSZRB3ROKQU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y6GjKwSZRB3ROKQU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-y6GjKwSZRB3ROKQU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-y6GjKwSZRB3ROKQU .cluster text{fill:#333;}#mermaid-svg-y6GjKwSZRB3ROKQU .cluster span{color:#333;}#mermaid-svg-y6GjKwSZRB3ROKQU 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-y6GjKwSZRB3ROKQU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-y6GjKwSZRB3ROKQU rect.text{fill:none;stroke-width:0;}#mermaid-svg-y6GjKwSZRB3ROKQU .icon-shape,#mermaid-svg-y6GjKwSZRB3ROKQU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y6GjKwSZRB3ROKQU .icon-shape p,#mermaid-svg-y6GjKwSZRB3ROKQU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-y6GjKwSZRB3ROKQU .icon-shape .label rect,#mermaid-svg-y6GjKwSZRB3ROKQU .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y6GjKwSZRB3ROKQU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-y6GjKwSZRB3ROKQU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-y6GjKwSZRB3ROKQU :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
是
否
是
否
newJitCDIModifier(automaticDevices)
f.image != nil?
附加 gatedDevices
newAutomaticCDISpecModifier(automaticDevices)
附加 imexDevices
获取CSV文件列表
image.Getenv(NVIDIA_REQUIRE_JETPACK)== 'csv-mounts=all'?
使用所有CSV文件
csv.BaseFilesOnly()仅基础文件
cdiModeIdentfiersFromDevices()提取模式标识符
对每个mode创建nvcdi实例
nvcdi.New(WithLogger,WithDriverRoot,WithVendor,WithClass,WithMode,…)
cdilib.GetSpec(ids)生成内存CDI spec
cdi.New(WithSpec(spec.Raw()))创建fromCDISpec修改器
追加到modifiers列表
还有模式?
返回 SpecModifiers
图8: hookRemover 流程图
#mermaid-svg-ReYylgOGn6vYR1Sw{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-ReYylgOGn6vYR1Sw .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ReYylgOGn6vYR1Sw .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ReYylgOGn6vYR1Sw .error-icon{fill:#552222;}#mermaid-svg-ReYylgOGn6vYR1Sw .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ReYylgOGn6vYR1Sw .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ReYylgOGn6vYR1Sw .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ReYylgOGn6vYR1Sw .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ReYylgOGn6vYR1Sw .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ReYylgOGn6vYR1Sw .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ReYylgOGn6vYR1Sw .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ReYylgOGn6vYR1Sw .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ReYylgOGn6vYR1Sw .marker.cross{stroke:#333333;}#mermaid-svg-ReYylgOGn6vYR1Sw svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ReYylgOGn6vYR1Sw p{margin:0;}#mermaid-svg-ReYylgOGn6vYR1Sw .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ReYylgOGn6vYR1Sw .cluster-label text{fill:#333;}#mermaid-svg-ReYylgOGn6vYR1Sw .cluster-label span{color:#333;}#mermaid-svg-ReYylgOGn6vYR1Sw .cluster-label span p{background-color:transparent;}#mermaid-svg-ReYylgOGn6vYR1Sw .label text,#mermaid-svg-ReYylgOGn6vYR1Sw span{fill:#333;color:#333;}#mermaid-svg-ReYylgOGn6vYR1Sw .node rect,#mermaid-svg-ReYylgOGn6vYR1Sw .node circle,#mermaid-svg-ReYylgOGn6vYR1Sw .node ellipse,#mermaid-svg-ReYylgOGn6vYR1Sw .node polygon,#mermaid-svg-ReYylgOGn6vYR1Sw .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ReYylgOGn6vYR1Sw .rough-node .label text,#mermaid-svg-ReYylgOGn6vYR1Sw .node .label text,#mermaid-svg-ReYylgOGn6vYR1Sw .image-shape .label,#mermaid-svg-ReYylgOGn6vYR1Sw .icon-shape .label{text-anchor:middle;}#mermaid-svg-ReYylgOGn6vYR1Sw .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ReYylgOGn6vYR1Sw .rough-node .label,#mermaid-svg-ReYylgOGn6vYR1Sw .node .label,#mermaid-svg-ReYylgOGn6vYR1Sw .image-shape .label,#mermaid-svg-ReYylgOGn6vYR1Sw .icon-shape .label{text-align:center;}#mermaid-svg-ReYylgOGn6vYR1Sw .node.clickable{cursor:pointer;}#mermaid-svg-ReYylgOGn6vYR1Sw .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ReYylgOGn6vYR1Sw .arrowheadPath{fill:#333333;}#mermaid-svg-ReYylgOGn6vYR1Sw .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ReYylgOGn6vYR1Sw .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ReYylgOGn6vYR1Sw .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ReYylgOGn6vYR1Sw .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ReYylgOGn6vYR1Sw .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ReYylgOGn6vYR1Sw .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ReYylgOGn6vYR1Sw .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ReYylgOGn6vYR1Sw .cluster text{fill:#333;}#mermaid-svg-ReYylgOGn6vYR1Sw .cluster span{color:#333;}#mermaid-svg-ReYylgOGn6vYR1Sw 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-ReYylgOGn6vYR1Sw .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ReYylgOGn6vYR1Sw rect.text{fill:none;stroke-width:0;}#mermaid-svg-ReYylgOGn6vYR1Sw .icon-shape,#mermaid-svg-ReYylgOGn6vYR1Sw .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ReYylgOGn6vYR1Sw .icon-shape p,#mermaid-svg-ReYylgOGn6vYR1Sw .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ReYylgOGn6vYR1Sw .icon-shape .label rect,#mermaid-svg-ReYylgOGn6vYR1Sw .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ReYylgOGn6vYR1Sw .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ReYylgOGn6vYR1Sw .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ReYylgOGn6vYR1Sw :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
是
否
是
否
是
否
是
否
是
否
nvidiaContainerRuntimeHookRemover.Modify(spec)
spec == nil?
返回 nil
spec.Hooks == nil?
len(Prestart) == 0?
遍历 spec.Hooks.Prestart
isNVIDIAContainerRuntimeHook?
Debug: 移除 hook跳过(不添加到newPrestart)
保留: 添加到 newPrestart
还有hook?
len(newPrestart) !=len(Prestart)?
更新 spec.Hooks.Prestart= newPrestart
不修改
返回 nil
图9: graphicsModifier 流程图
#mermaid-svg-6Cp1Ua2k8BmCfrl8{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-6Cp1Ua2k8BmCfrl8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .error-icon{fill:#552222;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .marker.cross{stroke:#333333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 p{margin:0;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .cluster-label text{fill:#333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .cluster-label span{color:#333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .cluster-label span p{background-color:transparent;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .label text,#mermaid-svg-6Cp1Ua2k8BmCfrl8 span{fill:#333;color:#333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .node rect,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .node circle,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .node ellipse,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .node polygon,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .rough-node .label text,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .node .label text,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .image-shape .label,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .rough-node .label,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .node .label,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .image-shape .label,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .icon-shape .label{text-align:center;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .node.clickable{cursor:pointer;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .arrowheadPath{fill:#333333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .cluster text{fill:#333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .cluster span{color:#333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 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-6Cp1Ua2k8BmCfrl8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .icon-shape,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .icon-shape p,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .icon-shape .label rect,#mermaid-svg-6Cp1Ua2k8BmCfrl8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6Cp1Ua2k8BmCfrl8 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
否
是
否
是
newGraphicsModifier()
requiresGraphicsModifier(*f.image)
有设备且需要graphics/display?
返回 nil, nil原因: 无设备/无能力
discover.NewGraphicsMountsDiscoverer()发现图形库挂载
成功?
返回错误
discover.NewDRMNodesDiscoverer()发现DRM设备节点
成功?
返回错误
discover.Merge(drmNodes, mounts)合并发现器
newModifierFromDiscoverer(merged)创建修改器
返回 discoverModifier
图10: gatedModifier 流程图
#mermaid-svg-ssxUwcqdBgHq7tf5{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-ssxUwcqdBgHq7tf5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ssxUwcqdBgHq7tf5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ssxUwcqdBgHq7tf5 .error-icon{fill:#552222;}#mermaid-svg-ssxUwcqdBgHq7tf5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ssxUwcqdBgHq7tf5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ssxUwcqdBgHq7tf5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ssxUwcqdBgHq7tf5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ssxUwcqdBgHq7tf5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ssxUwcqdBgHq7tf5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ssxUwcqdBgHq7tf5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ssxUwcqdBgHq7tf5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ssxUwcqdBgHq7tf5 .marker.cross{stroke:#333333;}#mermaid-svg-ssxUwcqdBgHq7tf5 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ssxUwcqdBgHq7tf5 p{margin:0;}#mermaid-svg-ssxUwcqdBgHq7tf5 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ssxUwcqdBgHq7tf5 .cluster-label text{fill:#333;}#mermaid-svg-ssxUwcqdBgHq7tf5 .cluster-label span{color:#333;}#mermaid-svg-ssxUwcqdBgHq7tf5 .cluster-label span p{background-color:transparent;}#mermaid-svg-ssxUwcqdBgHq7tf5 .label text,#mermaid-svg-ssxUwcqdBgHq7tf5 span{fill:#333;color:#333;}#mermaid-svg-ssxUwcqdBgHq7tf5 .node rect,#mermaid-svg-ssxUwcqdBgHq7tf5 .node circle,#mermaid-svg-ssxUwcqdBgHq7tf5 .node ellipse,#mermaid-svg-ssxUwcqdBgHq7tf5 .node polygon,#mermaid-svg-ssxUwcqdBgHq7tf5 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ssxUwcqdBgHq7tf5 .rough-node .label text,#mermaid-svg-ssxUwcqdBgHq7tf5 .node .label text,#mermaid-svg-ssxUwcqdBgHq7tf5 .image-shape .label,#mermaid-svg-ssxUwcqdBgHq7tf5 .icon-shape .label{text-anchor:middle;}#mermaid-svg-ssxUwcqdBgHq7tf5 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ssxUwcqdBgHq7tf5 .rough-node .label,#mermaid-svg-ssxUwcqdBgHq7tf5 .node .label,#mermaid-svg-ssxUwcqdBgHq7tf5 .image-shape .label,#mermaid-svg-ssxUwcqdBgHq7tf5 .icon-shape .label{text-align:center;}#mermaid-svg-ssxUwcqdBgHq7tf5 .node.clickable{cursor:pointer;}#mermaid-svg-ssxUwcqdBgHq7tf5 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ssxUwcqdBgHq7tf5 .arrowheadPath{fill:#333333;}#mermaid-svg-ssxUwcqdBgHq7tf5 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ssxUwcqdBgHq7tf5 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ssxUwcqdBgHq7tf5 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ssxUwcqdBgHq7tf5 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ssxUwcqdBgHq7tf5 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ssxUwcqdBgHq7tf5 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ssxUwcqdBgHq7tf5 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ssxUwcqdBgHq7tf5 .cluster text{fill:#333;}#mermaid-svg-ssxUwcqdBgHq7tf5 .cluster span{color:#333;}#mermaid-svg-ssxUwcqdBgHq7tf5 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-ssxUwcqdBgHq7tf5 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ssxUwcqdBgHq7tf5 rect.text{fill:none;stroke-width:0;}#mermaid-svg-ssxUwcqdBgHq7tf5 .icon-shape,#mermaid-svg-ssxUwcqdBgHq7tf5 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ssxUwcqdBgHq7tf5 .icon-shape p,#mermaid-svg-ssxUwcqdBgHq7tf5 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ssxUwcqdBgHq7tf5 .icon-shape .label rect,#mermaid-svg-ssxUwcqdBgHq7tf5 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ssxUwcqdBgHq7tf5 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ssxUwcqdBgHq7tf5 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ssxUwcqdBgHq7tf5 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
否
是
否
是
否
是
否
是
否
是
否
newFeatureGatedModifier()
image.VisibleDevices()为空?
返回 nil, nil
gatedDevices(*image).DeviceRequests()
检查环境变量:NVIDIA_GDS=enabledNVIDIA_MOFED=enabledNVIDIA_GDRCOPY=enabledNVIDIA_NVSWITCH=enabled
有gated请求?
newAutomaticCDISpecModifier()生成CDI spec修改器
跳过gated部分
添加到modifiers列表
DisableCUDACompatLibHook已启用?
跳过CUDA compat
getCudaCompatModeModifier()
Mode == 'csv'?
返回 nil (不支持)
Mode == 'legacy' 且CUDACompatMode != 'hook'?
返回 nil
driver.Version()获取驱动版本
discover.NewCUDACompatHookDiscoverer()
Mode == 'legacy'?
额外创建LDCacheUpdateHook
直接创建修改器
discover.Merge(compat, ldcache)
返回修改器
添加到modifiers列表
返回 list
图11: list.Modify() 遍历图
#mermaid-svg-ZZsGq3v6XGbj5tEc{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-ZZsGq3v6XGbj5tEc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZZsGq3v6XGbj5tEc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZZsGq3v6XGbj5tEc .error-icon{fill:#552222;}#mermaid-svg-ZZsGq3v6XGbj5tEc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZZsGq3v6XGbj5tEc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZZsGq3v6XGbj5tEc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZZsGq3v6XGbj5tEc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZZsGq3v6XGbj5tEc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZZsGq3v6XGbj5tEc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZZsGq3v6XGbj5tEc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZZsGq3v6XGbj5tEc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZZsGq3v6XGbj5tEc .marker.cross{stroke:#333333;}#mermaid-svg-ZZsGq3v6XGbj5tEc svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZZsGq3v6XGbj5tEc p{margin:0;}#mermaid-svg-ZZsGq3v6XGbj5tEc .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-ZZsGq3v6XGbj5tEc .cluster-label text{fill:#333;}#mermaid-svg-ZZsGq3v6XGbj5tEc .cluster-label span{color:#333;}#mermaid-svg-ZZsGq3v6XGbj5tEc .cluster-label span p{background-color:transparent;}#mermaid-svg-ZZsGq3v6XGbj5tEc .label text,#mermaid-svg-ZZsGq3v6XGbj5tEc span{fill:#333;color:#333;}#mermaid-svg-ZZsGq3v6XGbj5tEc .node rect,#mermaid-svg-ZZsGq3v6XGbj5tEc .node circle,#mermaid-svg-ZZsGq3v6XGbj5tEc .node ellipse,#mermaid-svg-ZZsGq3v6XGbj5tEc .node polygon,#mermaid-svg-ZZsGq3v6XGbj5tEc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ZZsGq3v6XGbj5tEc .rough-node .label text,#mermaid-svg-ZZsGq3v6XGbj5tEc .node .label text,#mermaid-svg-ZZsGq3v6XGbj5tEc .image-shape .label,#mermaid-svg-ZZsGq3v6XGbj5tEc .icon-shape .label{text-anchor:middle;}#mermaid-svg-ZZsGq3v6XGbj5tEc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ZZsGq3v6XGbj5tEc .rough-node .label,#mermaid-svg-ZZsGq3v6XGbj5tEc .node .label,#mermaid-svg-ZZsGq3v6XGbj5tEc .image-shape .label,#mermaid-svg-ZZsGq3v6XGbj5tEc .icon-shape .label{text-align:center;}#mermaid-svg-ZZsGq3v6XGbj5tEc .node.clickable{cursor:pointer;}#mermaid-svg-ZZsGq3v6XGbj5tEc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ZZsGq3v6XGbj5tEc .arrowheadPath{fill:#333333;}#mermaid-svg-ZZsGq3v6XGbj5tEc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ZZsGq3v6XGbj5tEc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ZZsGq3v6XGbj5tEc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZZsGq3v6XGbj5tEc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ZZsGq3v6XGbj5tEc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZZsGq3v6XGbj5tEc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ZZsGq3v6XGbj5tEc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ZZsGq3v6XGbj5tEc .cluster text{fill:#333;}#mermaid-svg-ZZsGq3v6XGbj5tEc .cluster span{color:#333;}#mermaid-svg-ZZsGq3v6XGbj5tEc 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-ZZsGq3v6XGbj5tEc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ZZsGq3v6XGbj5tEc rect.text{fill:none;stroke-width:0;}#mermaid-svg-ZZsGq3v6XGbj5tEc .icon-shape,#mermaid-svg-ZZsGq3v6XGbj5tEc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ZZsGq3v6XGbj5tEc .icon-shape p,#mermaid-svg-ZZsGq3v6XGbj5tEc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ZZsGq3v6XGbj5tEc .icon-shape .label rect,#mermaid-svg-ZZsGq3v6XGbj5tEc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ZZsGq3v6XGbj5tEc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ZZsGq3v6XGbj5tEc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ZZsGq3v6XGbj5tEc :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
否
是
是
否
是
否
list.Modify(spec)
初始化索引 i=0
i < len(list)?
返回 nil(所有修改器已应用)
list[i] == nil?
跳过i++
list[i].Modify(spec)
返回错误?
立即返回错误(不继续后续修改器)
图12: 四种模式对比图
#mermaid-svg-pAQuU1iOrIflraCP{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-pAQuU1iOrIflraCP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pAQuU1iOrIflraCP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pAQuU1iOrIflraCP .error-icon{fill:#552222;}#mermaid-svg-pAQuU1iOrIflraCP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pAQuU1iOrIflraCP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pAQuU1iOrIflraCP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pAQuU1iOrIflraCP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pAQuU1iOrIflraCP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pAQuU1iOrIflraCP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pAQuU1iOrIflraCP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pAQuU1iOrIflraCP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pAQuU1iOrIflraCP .marker.cross{stroke:#333333;}#mermaid-svg-pAQuU1iOrIflraCP svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pAQuU1iOrIflraCP p{margin:0;}#mermaid-svg-pAQuU1iOrIflraCP .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-pAQuU1iOrIflraCP .cluster-label text{fill:#333;}#mermaid-svg-pAQuU1iOrIflraCP .cluster-label span{color:#333;}#mermaid-svg-pAQuU1iOrIflraCP .cluster-label span p{background-color:transparent;}#mermaid-svg-pAQuU1iOrIflraCP .label text,#mermaid-svg-pAQuU1iOrIflraCP span{fill:#333;color:#333;}#mermaid-svg-pAQuU1iOrIflraCP .node rect,#mermaid-svg-pAQuU1iOrIflraCP .node circle,#mermaid-svg-pAQuU1iOrIflraCP .node ellipse,#mermaid-svg-pAQuU1iOrIflraCP .node polygon,#mermaid-svg-pAQuU1iOrIflraCP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pAQuU1iOrIflraCP .rough-node .label text,#mermaid-svg-pAQuU1iOrIflraCP .node .label text,#mermaid-svg-pAQuU1iOrIflraCP .image-shape .label,#mermaid-svg-pAQuU1iOrIflraCP .icon-shape .label{text-anchor:middle;}#mermaid-svg-pAQuU1iOrIflraCP .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pAQuU1iOrIflraCP .rough-node .label,#mermaid-svg-pAQuU1iOrIflraCP .node .label,#mermaid-svg-pAQuU1iOrIflraCP .image-shape .label,#mermaid-svg-pAQuU1iOrIflraCP .icon-shape .label{text-align:center;}#mermaid-svg-pAQuU1iOrIflraCP .node.clickable{cursor:pointer;}#mermaid-svg-pAQuU1iOrIflraCP .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pAQuU1iOrIflraCP .arrowheadPath{fill:#333333;}#mermaid-svg-pAQuU1iOrIflraCP .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pAQuU1iOrIflraCP .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pAQuU1iOrIflraCP .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pAQuU1iOrIflraCP .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pAQuU1iOrIflraCP .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pAQuU1iOrIflraCP .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pAQuU1iOrIflraCP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pAQuU1iOrIflraCP .cluster text{fill:#333;}#mermaid-svg-pAQuU1iOrIflraCP .cluster span{color:#333;}#mermaid-svg-pAQuU1iOrIflraCP 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-pAQuU1iOrIflraCP .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pAQuU1iOrIflraCP rect.text{fill:none;stroke-width:0;}#mermaid-svg-pAQuU1iOrIflraCP .icon-shape,#mermaid-svg-pAQuU1iOrIflraCP .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pAQuU1iOrIflraCP .icon-shape p,#mermaid-svg-pAQuU1iOrIflraCP .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pAQuU1iOrIflraCP .icon-shape .label rect,#mermaid-svg-pAQuU1iOrIflraCP .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pAQuU1iOrIflraCP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pAQuU1iOrIflraCP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pAQuU1iOrIflraCP :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
jit-cdi 模式
1. nvidia-hook-remover(移除旧hook)
2. cdiModifier(fromCDISpec)(运行时生成CDI spec)
特点: – 移除旧hook – 运行时即时生成CDI spec – 使用nvcdi库 – 无需预生成spec文件 – 默认模式
cdi 模式
1. nvidia-hook-remover(移除旧hook)
2. cdiModifier(fromRegistry)(从CDI spec文件读取)
特点: – 移除旧hook – 从预生成的CDI spec注入 – CDI spec由nvidia-cdi-hook生成 – 标准CDI方式
csv 模式
1. nvidia-hook-remover(移除旧hook)
2. feature-gated(GDS/MOFED/CUDA-compat)
3. csvModifier(基于CSV生成CDI spec)
特点: – 移除Docker注入的旧hook – 从CSV文件读取设备/挂载 – 生成内存CDI spec – 主要用于Tegra平台
legacy 模式
1. feature-gated(GDS/MOFED/CUDA-compat)
2. graphics(DRM设备+图形库)
3. stableRuntimeModifier(注入prestart hook)
特点: – 不移除旧hook – 注入nvidia-container-runtime-hook – hook执行nvidia-container-cli – 最传统的修改方式
五、关键设计模式分析
5.1 工厂模式(Factory Pattern)
Factory 是修改器模块的核心工厂:
type Factory struct {
factoryOptions
editsFactory edits.Factory
}
func New(opts …Option) (oci.SpecModifier, error) {
f := createFactory(opts…)
if err := f.validate(); err != nil {
return nil, err
}
return f, nil
}
优点:
- 集中管理所有修改器的创建
- 验证配置有效性
- Factory 自身实现 SpecModifier,接口一致
5.2 策略模式(Strategy Pattern)
newModeModifier() 根据运行时模式选择不同策略:
switch f.runtimeMode {
case info.LegacyRuntimeMode:
return f.newStableRuntimeModifier(), nil
case info.CSVRuntimeMode:
return f.newCSVModifier()
case info.CDIRuntimeMode, info.JitCDIRuntimeMode:
return f.newCDIModifier(…)
}
5.3 列表/组合模式(Composite Pattern)
list 类型将多个修改器组合为一个:
type list []oci.SpecModifier
func (m list) Modify(spec *specs.Spec) error {
for _, mm := range m {
if mm == nil { continue }
if err := mm.Modify(spec); err != nil { return err }
}
return nil
}
5.4 装饰器模式
deduplicatedDeviceRequestor 包装 deviceRequestor,添加去重功能:
type deduplicatedDeviceRequestor struct {
deviceRequestor
}
func withUniqueDevices(d deviceRequestor) deviceRequestor {
return &deduplicatedDeviceRequestor{deviceRequestor: d}
}
5.5 构建器模式(Builder Pattern)
cdi/builder.go 使用构建器创建 CDI 修改器:
type builder struct {
logger logger.Interface
specDirs []string
devices []string
cdiSpec *specs.Spec
}
func New(opts …Option) (oci.SpecModifier, error) {
b := &builder{}
for _, opt := range opts {
opt(b)
}
return b.build()
}
六、数据流分析
6.1 修改器创建数据流
runtime.newSpecModifier()
→ modifier.New(
WithLogger(logger),
WithConfig(cfg),
WithImage(image), // CUDA 镜像
WithDriver(driver), // 驱动根路径
WithHookCreator(hookCreator),
WithRuntimeMode(mode), // "legacy"/"csv"/"cdi"/"jit-cdi"
)
→ createFactory(opts…)
→ Factory{
logger, cfg, driver, hookCreator, image, runtimeMode,
editsFactory,
}
→ validate()
→ 返回 Factory 作为 SpecModifier
当 spec.Modify(factory) 被调用:
→ factory.Modify(spec)
→ factory.create()
→ supportedModifierTypes(mode)
→ ["feature-gated", "graphics", "mode"] // legacy
→ for each type:
"mode" → newModeModifier() → stableRuntimeModifier
"graphics" → newGraphicsModifier() → discoverModifier
"feature-gated" → newFeatureGatedModifier() → list
→ 返回 list[stableRuntime, graphics, featureGated]
→ list.Modify(spec)
→ stableRuntime.Modify(spec) → 注入 prestart hook
→ graphics.Modify(spec) → 注入 DRM 设备 + 图形库
→ featureGated.Modify(spec) → 注入 GDS/MOFED + CUDA compat
6.2 设备请求数据流
CUDA 镜像环境变量:
NVIDIA_VISIBLE_DEVICES=all
NVIDIA_DRIVER_CAPABILITIES=compute,utility
NVIDIA_GDS=enabled
NVIDIA_MOFED=enabled
↓ cdiDeviceRequestor.DeviceRequests()
CDI 设备请求:
["runtime.nvidia.com/gpu=all"] // JIT-CDI 模式
或
["nvidia.com/gpu=all"] // CDI 模式
↓ gatedDevices.DeviceRequests()
Gated 设备请求:
["mode=gds", "mode=mofed"]
↓ cdiModeIdentfiersFromDevices()
模式标识符:
{
modes: ["auto", "gds", "mofed"],
idsByMode: {
"auto": ["all"],
"gds": [],
"mofed": [],
},
deviceClassByMode: {"auto": "gpu"},
}
↓ 对每个模式创建 nvcdi 实例
nvcdi 生成 CDI spec:
– mode="auto": 生成 GPU 设备的 CDI spec
– mode="gds": 生成 GDS 设备的 CDI spec
– mode="mofed": 生成 MOFED 设备的 CDI spec
↓ cdi.New(WithSpec(spec.Raw()))
fromCDISpec 修改器:
– ApplyEdits(spec) → 注入设备节点、挂载、hook
七、测试覆盖分析
7.1 stable_test.go — TestAddHookModifier
| 空 spec | {} | 添加 hook |
| 空 hooks | Hooks: {} | 添加 hook |
| 已有 hook | Hooks: {Prestart: [nvidia-hook]} | 不修改(幂等) |
| 其他 hook | Hooks: {Prestart: [some-hook]} | 追加 NVIDIA hook |
7.2 hook_remover_test.go — TestHookRemover
| 非 NVIDIA hook | Prestart: [/hook/a] | 保留 |
| nvidia-container-runtime-hook | Prestart: [/path/nvidia-container-runtime-hook] | 移除 |
| nvidia-container-toolkit | Prestart: [/path/nvidia-container-toolkit] | 移除 |
7.3 cdi_test.go — TestDeviceRequests
| 空 spec | nil input | [] |
| CDI 从挂载 | volume mount devices | [“nvidia.com/gpu=0”, “nvidia.com/gpu=1”] |
| CDI 从环境变量 | NVD_VISIBLE_DEVICES=0,example.com/class=device | [“nvidia.com/gpu=0”, “example.com/class=device”] |
| 自动 kind | defaultKind=“runtime.nvidia.com/gpu” | [“runtime.nvidia.com/gpu=all”] |
| annotation | prefix/foo=example.com/device=bar | [“example.com/device=bar”] |
| NVD=none | NVD_VISIBLE_DEVICES=none | [“runtime.nvidia.com/gpu=none”] |
| Swarm | SWARM_RESOURCE=GPU1 | [“runtime.nvidia.com/gpu=GPU1”] |
7.4 cdi_test.go — Test_cdiModeIdentfiersFromDevices
| [] | nil | {} |
| [“0”] | [“auto”] | {“auto”: [“0”]} |
| [“0”, “1”] | [“auto”] | {“auto”: [“0”,“1”]} |
| [“mode=gds,id=foo”] | [“gds”] | {“gds”: [“foo”]} |
| [“0”, “mode=gds,id=foo”, “mode=gdrcopy,id=bar”] | [“auto”,“gds”,“gdrcopy”] | {“auto”:[“0”],“gds”:[“foo”],“gdrcopy”:[“bar”]} |
| [“mode=nvswitch”] | [“nvswitch”] | {} |
| [“none”] | [“auto”] | {“auto”: [“none”]} |
7.5 graphics_test.go — TestGraphicsModifier
| 无 | [] |
| NVD_VISIBLE_DEVICES=all | [] (无能力) |
| NVD=all + CAPS=compute | [] (非图形) |
| NVD=all + CAPS=all | [“all”] |
| NVD=all + CAPS=graphics | [“all”] |
| NVD=all + CAPS=display | [“all”] |
7.6 discover_test.go — TestDiscoverModifier
| 空发现器 | 无 hooks/devices/mounts | 不变 |
| Hooks 错误 | error | 返回错误 |
| 有 hooks | prestart + createContainer hooks | 注入到 spec.Hooks |
| 已有 hooks | Prestart: [/hook/a] + 新 [/hook/b] | 追加 |
八、性能分析
| Factory.create() | <1ms | 创建修改器列表 |
| stableRuntimeModifier.Modify() | <1ms | 仅追加 hook |
| hookRemover.Modify() | <1ms | 遍历和过滤 hooks |
| csvModifier 创建 | ~50-200ms | 包含 CSV 解析和 CDI spec 生成 |
| cdiModifier (fromRegistry) | ~10-50ms | 从文件系统读取 CDI spec |
| cdiModifier (fromCDISpec) | ~20-100ms | 运行时生成 CDI spec |
| graphicsModifier | ~10-50ms | 发现 DRM 设备和图形库 |
| gatedModifier | ~10-100ms | 可能涉及 CDI spec 生成 |
| discoverModifier.Modify() | ~5-20ms | 从发现器创建并应用编辑 |
九、安全分析
9.1 输入验证
| 无效运行时模式 | validate() 检查模式有效性 |
| 空配置 | validate() 检查 driver 非 nil |
| 无效设备名 | parser.IsQualifiedName 验证 CDI 设备名 |
| 混合自动/手动设备 | filterAutomaticDevices + 长度比较检测 |
9.2 CUDA 需求检查
checkRequirements 验证容器镜像的 CUDA 需求与主机兼容:
- 检查 CUDA 版本兼容性
- 检查计算能力兼容性
- 可通过 NVIDIA_DISABLE_REQUIRE 跳过
9.3 Hook 路径安全
- hook 路径在 runtime.Run() 中已解析为绝对路径
- isNVIDIAContainerRuntimeHook 通过 filepath.Base 比较基础名
- 不依赖完整路径匹配,减少路径遍历风险
十、总结
10.1 模块核心价值
internal/modifier 模块是 NVIDIA Container Toolkit 的修改引擎,负责将 GPU 资源注入容器配置:
10.2 设计亮点
10.3 模式演进
legacy (最早期)
↓ 通过 hook 调用 nvidia-container-cli
↓ 简单但不够标准化
csv (Tegra 平台)
↓ 基于 CSV 文件定义设备
↓ 生成内存 CDI spec
↓ 适合 Tegra 等非标准平台
cdi (标准化)
↓ 使用预生成的 CDI spec 文件
↓ 符合 CDI 标准
↓ 与其他 CDI 客户端兼容
jit-cdi (默认模式)
↓ 运行时即时生成 CDI spec
↓ 无需预生成
↓ 自动适应设备变化
↓ 当前推荐模式
10.4 与其他模块的协作
modifier 模块是 runtime 和具体修改实现之间的中间层:
- runtime 通过 modifier.New() 创建修改器
- 修改器实现 oci.SpecModifier 接口
- OCI 模块调用 Spec.Modify(modifier) 应用修改
- 修改器内部使用 discover、edits、nvcdi 等库完成具体工作
这种分层设计使得各模块职责清晰,修改器可以独立演进,不影响 runtime 和 OCI 层。


