欢迎光临
我们一直在努力

【NVIDA】NVIDIA Container Toolkit v1.19.1 — Modifier 模块超深度分析之四

NVIDIA Container Toolkit v1.19.1 — Modifier 模块超深度分析

源码路径:internal/modifier/ 版本:v1.19.1 文档定位:Go源码架构逐行分析系列 · 第四篇


一、模块定位

1.1 模块职责

internal/modifier 模块是 NVIDIA Container Toolkit 的 OCI Spec 修改器引擎,负责修改容器配置以注入 GPU 资源。其核心职责包括:

  • Factory 模式管理修改器:通过 Factory 统一创建和管理各种修改器
  • 模式分发:根据运行时模式(legacy/csv/cdi/jit-cdi)选择不同的修改策略
  • Hook 注入:向 OCI spec 注入 prestart/createContainer hook
  • 设备注入:向 OCI spec 注入 GPU 设备节点、挂载点、环境变量
  • Hook 清理:移除旧的 nvidia-container-runtime-hook
  • 图形支持:注入图形库挂载和 DRM 设备
  • 功能门控:基于环境变量注入 GDS/MOFED/GDRCOPY/NVSWITCH 支持
  • CDI 设备注入:通过 CDI 规范注入设备
  • JIT CDI 生成:运行时即时生成 CDI spec 并注入设备
  • 列表组合:将多个修改器组合为列表,按顺序执行
  • 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 四种运行时模式对比

    模式修改器组合设备注入方式Hook 处理
    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预期结果
    空 spec {} 添加 hook
    空 hooks Hooks: {} 添加 hook
    已有 hook Hooks: {Prestart: [nvidia-hook]} 不修改(幂等)
    其他 hook Hooks: {Prestart: [some-hook]} 追加 NVIDIA hook

    7.2 hook_remover_test.go — TestHookRemover

    测试用例初始 spec预期结果
    非 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

    输入预期 modes预期 idsByMode
    [] 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

    场景发现器返回预期 spec
    空发现器 无 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 资源注入容器配置:

  • 多模式支持:legacy/csv/cdi/jit-cdi 四种模式,适应不同场景
  • 组合修改:通过 list 类型组合多个修改器,按顺序应用
  • CDI 标准兼容:支持 CDI (Container Device Interface) 标准
  • JIT 生成:运行时即时生成 CDI spec,无需预生成
  • 功能门控:基于环境变量灵活启用可选功能
  • 10.2 设计亮点

  • Factory 实现 SpecModifier:工厂自身也是修改器,接口一致
  • supportedModifierTypes 分离:模式与修改器类型解耦
  • withUniqueDevices 装饰器:优雅的去重功能
  • cdiModeIdentifiers:灵活的设备-模式映射
  • 幂等性设计:stableRuntimeModifier 检查已有 hook
  • 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 层。

    赞(0)
    未经允许不得转载:171主机测评 » 【NVIDA】NVIDIA Container Toolkit v1.19.1 — Modifier 模块超深度分析之四
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址