【NVIDIA】k8s-device-plugin v0.19.3 辅助命令模块深度分析之七

NVIDIA k8s-device-plugin v0.19.3 辅助命令模块深度分析

分析范围:cmd/gpu-feature-discoverycmd/config-managercmd/mps-control-daemon(含 mps 子包及 mount 子包)

版本:v0.19.3

日期:2026-07-26


目录

  1. 模块总览
  2. [GPU Feature Discovery (GFD)](#GPU Feature Discovery (GFD))
  3. [Config Manager](#Config Manager)
  4. [MPS Control Daemon](#MPS Control Daemon)
  5. [MPS Daemon 核心逻辑](#MPS Daemon 核心逻辑)
  6. [MPS Device 设备校验](#MPS Device 设备校验)
  7. [MPS Log Tailer 日志跟踪](#MPS Log Tailer 日志跟踪)
  8. [MPS Manager 守护进程管理器](#MPS Manager 守护进程管理器)
  9. [MPS Options 配置选项](#MPS Options 配置选项)
  10. [MPS Root 路径管理](#MPS Root 路径管理)
  11. [Mount SHM 共享内存挂载](#Mount SHM 共享内存挂载)
  12. 全局架构与交互关系

1. 模块总览

1.1 辅助命令在系统中的位置

k8s-device-plugin 除了核心的 nvidia-device-plugin 守护进程外,还提供了三个辅助命令,各自承担独立的运营职责:

命令 路径 核心职责
gpu-feature-discovery (GFD) cmd/gpu-feature-discovery/ 发现 GPU 硬件特征,生成节点标签
config-manager cmd/config-manager/ 监听 Node 标签变化,动态切换设备插件配置
mps-control-daemon cmd/mps-control-daemon/ 管理 MPS (Multi-Process Service) 守护进程,实现 GPU 共享

1.2 模块关系总览

#mermaid-svg-FgaN3GtCJSWj1Zz8{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-FgaN3GtCJSWj1Zz8 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .error-icon{fill:#552222;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .marker.cross{stroke:#333333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 p{margin:0;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster-label text{fill:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster-label span{color:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster-label span p{background-color:transparent;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .label text,#mermaid-svg-FgaN3GtCJSWj1Zz8 span{fill:#333;color:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .node rect,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node circle,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node ellipse,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node polygon,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .rough-node .label text,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node .label text,#mermaid-svg-FgaN3GtCJSWj1Zz8 .image-shape .label,#mermaid-svg-FgaN3GtCJSWj1Zz8 .icon-shape .label{text-anchor:middle;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .rough-node .label,#mermaid-svg-FgaN3GtCJSWj1Zz8 .node .label,#mermaid-svg-FgaN3GtCJSWj1Zz8 .image-shape .label,#mermaid-svg-FgaN3GtCJSWj1Zz8 .icon-shape .label{text-align:center;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .node.clickable{cursor:pointer;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .arrowheadPath{fill:#333333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FgaN3GtCJSWj1Zz8 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FgaN3GtCJSWj1Zz8 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster text{fill:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .cluster span{color:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 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-FgaN3GtCJSWj1Zz8 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FgaN3GtCJSWj1Zz8 rect.text{fill:none;stroke-width:0;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .icon-shape,#mermaid-svg-FgaN3GtCJSWj1Zz8 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .icon-shape p,#mermaid-svg-FgaN3GtCJSWj1Zz8 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .icon-shape .label rect,#mermaid-svg-FgaN3GtCJSWj1Zz8 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FgaN3GtCJSWj1Zz8 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FgaN3GtCJSWj1Zz8 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FgaN3GtCJSWj1Zz8 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外部依赖
Kubernetes Node
符号链接配置文件
SIGHUP 信号
节点标签
MPS 共享
config-manager
nvidia-device-plugin

核心插件
gpu-feature-discovery
mps-control-daemon
Kubernetes API
NVML 库
nvidia-smi
nvidia-cuda-mps-control


2. GPU Feature Discovery (GFD)

2.1 模块定位

GFD 是一个 Node Feature Discovery (NFD) 兼容的 GPU 特征发现工具。它定期扫描节点上的 GPU 硬件,将 GPU 的型号、驱动版本、CUDA 版本、MIG 能力等信息转化为 Kubernetes 节点标签,供调度器使用。

2.2 文件结构

复制代码
cmd/gpu-feature-discovery/
└── main.go    # 单文件实现,约 270 行

2.3 CLI 配置项

Flag 默认值 环境变量 用途
--mig-strategy none GFD_MIG_STRATEGY MIG 设备暴露策略
--fail-on-init-error true GFD_FAIL_ON_INIT_ERROR 初始化失败时是否退出
--oneshot false GFD_ONESHOT 标注一次后退出
--no-timestamp false GFD_NO_TIMESTAMP 不添加时间戳标签
--sleep-interval 60s GFD_SLEEP_INTERVAL 标注间隔
--output-file /etc/kubernetes/.../gfd GFD_OUTPUT_FILE 输出文件路径
--machine-type-file /sys/class/dmi/id/product_name GFD_MACHINE_TYPE_FILE DMI 信息文件
--config-file - GFD_CONFIG_FILE 配置文件路径
--use-node-feature-api true GFD_USE_NODE_FEATURE_API 使用 NFD NodeFeature API
--device-discovery-strategy auto DEVICE_DISCOVERY_STRATEGY 设备发现策略

2.4 核心数据结构

go 复制代码
// Config 收集 GFD 的 CLI 配置
type Config struct {
    configFile      string
    kubeClientConfig flags.KubeClientConfig  // K8s 客户端配置
    nodeConfig       flags.NodeConfig         // 节点配置
    flags            []cli.Flag               // CLI flag 列表
}

// gfd 运行时实例
type gfd struct {
    manager       resource.Manager   // 资源管理器
    vgpu          vgpu.Interface      // vGPU 接口
    config        *spec.Config        // 规格配置
    labelOutputer lm.Outputer         // 标签输出器
}

2.5 执行流程深度解析

2.5.1 main() 函数

#mermaid-svg-TXEVRsBSOlvEjNMe{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-TXEVRsBSOlvEjNMe .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TXEVRsBSOlvEjNMe .error-icon{fill:#552222;}#mermaid-svg-TXEVRsBSOlvEjNMe .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TXEVRsBSOlvEjNMe .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TXEVRsBSOlvEjNMe .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TXEVRsBSOlvEjNMe .marker.cross{stroke:#333333;}#mermaid-svg-TXEVRsBSOlvEjNMe svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TXEVRsBSOlvEjNMe p{margin:0;}#mermaid-svg-TXEVRsBSOlvEjNMe .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster-label text{fill:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster-label span{color:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster-label span p{background-color:transparent;}#mermaid-svg-TXEVRsBSOlvEjNMe .label text,#mermaid-svg-TXEVRsBSOlvEjNMe span{fill:#333;color:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe .node rect,#mermaid-svg-TXEVRsBSOlvEjNMe .node circle,#mermaid-svg-TXEVRsBSOlvEjNMe .node ellipse,#mermaid-svg-TXEVRsBSOlvEjNMe .node polygon,#mermaid-svg-TXEVRsBSOlvEjNMe .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TXEVRsBSOlvEjNMe .rough-node .label text,#mermaid-svg-TXEVRsBSOlvEjNMe .node .label text,#mermaid-svg-TXEVRsBSOlvEjNMe .image-shape .label,#mermaid-svg-TXEVRsBSOlvEjNMe .icon-shape .label{text-anchor:middle;}#mermaid-svg-TXEVRsBSOlvEjNMe .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TXEVRsBSOlvEjNMe .rough-node .label,#mermaid-svg-TXEVRsBSOlvEjNMe .node .label,#mermaid-svg-TXEVRsBSOlvEjNMe .image-shape .label,#mermaid-svg-TXEVRsBSOlvEjNMe .icon-shape .label{text-align:center;}#mermaid-svg-TXEVRsBSOlvEjNMe .node.clickable{cursor:pointer;}#mermaid-svg-TXEVRsBSOlvEjNMe .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TXEVRsBSOlvEjNMe .arrowheadPath{fill:#333333;}#mermaid-svg-TXEVRsBSOlvEjNMe .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TXEVRsBSOlvEjNMe .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TXEVRsBSOlvEjNMe .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TXEVRsBSOlvEjNMe .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TXEVRsBSOlvEjNMe .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TXEVRsBSOlvEjNMe .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster text{fill:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe .cluster span{color:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe 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-TXEVRsBSOlvEjNMe .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TXEVRsBSOlvEjNMe rect.text{fill:none;stroke-width:0;}#mermaid-svg-TXEVRsBSOlvEjNMe .icon-shape,#mermaid-svg-TXEVRsBSOlvEjNMe .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TXEVRsBSOlvEjNMe .icon-shape p,#mermaid-svg-TXEVRsBSOlvEjNMe .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TXEVRsBSOlvEjNMe .icon-shape .label rect,#mermaid-svg-TXEVRsBSOlvEjNMe .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TXEVRsBSOlvEjNMe .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TXEVRsBSOlvEjNMe .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TXEVRsBSOlvEjNMe :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否

main 开始
创建 Config 结构
创建 cli.App
设置 App 属性

Name/Usage/Version/Action
定义 CLI flags
追加 kubeClient + node flags
c.Run 执行
运行成功?
klog.Error + os.Exit 1
正常退出

逐行解析 main()

go 复制代码
func main() {
    config := &Config{}  // 初始化空配置结构

    c := cli.NewApp()   // 创建 urfave/cli 应用实例
    c.Name = "GPU Feature Discovery"
    c.Usage = "generate labels for NVIDIA devices"
    c.Version = info.GetVersionString()  // 从内部 info 包获取版本
    c.Action = func(ctx *cli.Context) error {
        return start(ctx, config)  // 入口 action 调用 start()
    }

    // 定义所有 CLI flags(见 2.3 表格)
    config.flags = []cli.Flag{
        &cli.StringFlag{ Name: "mig-strategy", Value: spec.MigStrategyNone, ... },
        &cli.BoolFlag{  Name: "fail-on-init-error", Value: true, ... },
        &cli.BoolFlag{  Name: "oneshot", Value: false, ... },
        &cli.BoolFlag{  Name: "no-timestamp", Value: false, ... },
        &cli.GenericFlag{ Name: "sleep-interval", Value: spec.NewDurationValue(60*time.Second), ... },
        &cli.StringFlag{ Name: "output-file", Value: "/etc/kubernetes/node-feature-discovery/features.d/gfd", ... },
        &cli.StringFlag{ Name: "machine-type-file", Value: "/sys/class/dmi/id/product_name", ... },
        &cli.StringFlag{ Name: "config-file", Destination: &config.configFile, ... },
        &cli.BoolFlag{  Name: "use-node-feature-api", Value: true, ... },
        &cli.StringFlag{ Name: "device-discovery-strategy", Value: "auto", ... },
        &cli.StringFlag{ Name: "driver-root-ctr-path", Value: spec.DefaultContainerDriverRoot, ... },
    }

    // 追加 Kubernetes 客户端和节点相关 flags
    config.flags = append(config.flags, config.kubeClientConfig.Flags()...)
    config.flags = append(config.flags, config.nodeConfig.Flags()...)
    c.Flags = config.flags

    if err := c.Run(os.Args); err != nil {  // 解析参数并执行
        klog.Error(err)
        os.Exit(1)
    }
}
2.5.2 start() 函数 --- 主循环

#mermaid-svg-4uU0Y8jIZvF5eXrs{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-4uU0Y8jIZvF5eXrs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4uU0Y8jIZvF5eXrs .error-icon{fill:#552222;}#mermaid-svg-4uU0Y8jIZvF5eXrs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4uU0Y8jIZvF5eXrs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .marker.cross{stroke:#333333;}#mermaid-svg-4uU0Y8jIZvF5eXrs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4uU0Y8jIZvF5eXrs p{margin:0;}#mermaid-svg-4uU0Y8jIZvF5eXrs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster-label text{fill:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster-label span{color:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster-label span p{background-color:transparent;}#mermaid-svg-4uU0Y8jIZvF5eXrs .label text,#mermaid-svg-4uU0Y8jIZvF5eXrs span{fill:#333;color:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .node rect,#mermaid-svg-4uU0Y8jIZvF5eXrs .node circle,#mermaid-svg-4uU0Y8jIZvF5eXrs .node ellipse,#mermaid-svg-4uU0Y8jIZvF5eXrs .node polygon,#mermaid-svg-4uU0Y8jIZvF5eXrs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .rough-node .label text,#mermaid-svg-4uU0Y8jIZvF5eXrs .node .label text,#mermaid-svg-4uU0Y8jIZvF5eXrs .image-shape .label,#mermaid-svg-4uU0Y8jIZvF5eXrs .icon-shape .label{text-anchor:middle;}#mermaid-svg-4uU0Y8jIZvF5eXrs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .rough-node .label,#mermaid-svg-4uU0Y8jIZvF5eXrs .node .label,#mermaid-svg-4uU0Y8jIZvF5eXrs .image-shape .label,#mermaid-svg-4uU0Y8jIZvF5eXrs .icon-shape .label{text-align:center;}#mermaid-svg-4uU0Y8jIZvF5eXrs .node.clickable{cursor:pointer;}#mermaid-svg-4uU0Y8jIZvF5eXrs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .arrowheadPath{fill:#333333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4uU0Y8jIZvF5eXrs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-4uU0Y8jIZvF5eXrs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4uU0Y8jIZvF5eXrs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster text{fill:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs .cluster span{color:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs 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-4uU0Y8jIZvF5eXrs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-4uU0Y8jIZvF5eXrs rect.text{fill:none;stroke-width:0;}#mermaid-svg-4uU0Y8jIZvF5eXrs .icon-shape,#mermaid-svg-4uU0Y8jIZvF5eXrs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-4uU0Y8jIZvF5eXrs .icon-shape p,#mermaid-svg-4uU0Y8jIZvF5eXrs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-4uU0Y8jIZvF5eXrs .icon-shape .label rect,#mermaid-svg-4uU0Y8jIZvF5eXrs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-4uU0Y8jIZvF5eXrs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-4uU0Y8jIZvF5eXrs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-4uU0Y8jIZvF5eXrs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



start 开始
启动 OS 信号监听

SIGHUP/SIGINT/SIGTERM/SIGQUIT
加载配置 loadConfig
禁用 Resource Naming
打印配置 JSON
初始化 NVML/device/info 库
创建 ResourceManager
创建 vGPU Lib
UseNodeFeatureAPI?
创建 K8s ClientSets
跳过
创建 LabelOutputer
创建 gfd 实例
d.run 执行标注循环
restart?
返回 nil 退出

关键代码段解析:

go 复制代码
func start(c *cli.Context, cfg *Config) error {
    defer func() { klog.Info("Exiting") }()

    // 启动 OS 信号 watcher,监听 4 种信号
    sigs := watch.Signals(syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

    for {  // 外层循环:SIGHUP 后重新加载配置重启
        klog.Info("Loading configuration.")
        config, err := cfg.loadConfig(c)  // 从 CLI flags + config file 加载
        if err != nil {
            return fmt.Errorf("unable to load config: %v", err)
        }
        spec.DisableResourceNamingInConfig(config)  // 禁用资源命名(GFD 不需要)

        // 打印最终配置 JSON,便于调试
        configJSON, _ := json.MarshalIndent(config, "", "  ")
        klog.Infof("\nRunning with config:\n%v", string(configJSON))

        // 初始化三层库:nvml(底层 NVML 绑定)→ device(设备抽象)→ info(信息聚合)
        nvmllib := nvml.New()
        devicelib := device.New(nvmllib)
        infolib := nvinfo.New(
            nvinfo.WithNvmlLib(nvmllib),
            nvinfo.WithDeviceLib(devicelib),
        )

        // 创建资源管理器:根据配置和设备信息管理 GPU 资源
        manager, err := resource.NewManager(infolib, nvmllib, devicelib, config)
        if err != nil {
            return fmt.Errorf("failed to create resource manager: %w", err)
        }

        vgpul := vgpu.NewVGPULib(vgpu.NewNvidiaPCILib())  // vGPU PCI 库

        // 如果启用 NodeFeature API,创建 K8s 客户端
        var clientSets flags.ClientSets
        if config.Flags.UseNodeFeatureAPI != nil && *config.Flags.UseNodeFeatureAPI {
            cs, err := cfg.kubeClientConfig.NewClientSets()
            if err != nil {
                return fmt.Errorf("failed to create clientsets: %w", err)
            }
            clientSets = cs
        }

        // 创建标签输出器:决定标签写到文件还是 NodeFeature API
        labelOutputer, err := lm.NewOutputer(config, cfg.nodeConfig, clientSets)
        if err != nil {
            return fmt.Errorf("failed to create label outputer: %w", err)
        }

        // 组装运行时实例
        d := &gfd{
            manager:       manager,
            vgpu:          vgpul,
            config:        config,
            labelOutputer: labelOutputer,
        }
        restart, err := d.run(sigs)
        if err != nil {
            return err
        }
        if !restart {
            return nil  // 正常退出
        }
        // restart=true 时回到 for 循环顶部,重新加载配置
    }
}
2.5.3 gfd.run() --- 标注执行循环

#mermaid-svg-9NVejWnLTa6Cd9YT{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-9NVejWnLTa6Cd9YT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9NVejWnLTa6Cd9YT .error-icon{fill:#552222;}#mermaid-svg-9NVejWnLTa6Cd9YT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9NVejWnLTa6Cd9YT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9NVejWnLTa6Cd9YT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9NVejWnLTa6Cd9YT .marker.cross{stroke:#333333;}#mermaid-svg-9NVejWnLTa6Cd9YT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9NVejWnLTa6Cd9YT p{margin:0;}#mermaid-svg-9NVejWnLTa6Cd9YT .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster-label text{fill:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster-label span{color:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster-label span p{background-color:transparent;}#mermaid-svg-9NVejWnLTa6Cd9YT .label text,#mermaid-svg-9NVejWnLTa6Cd9YT span{fill:#333;color:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT .node rect,#mermaid-svg-9NVejWnLTa6Cd9YT .node circle,#mermaid-svg-9NVejWnLTa6Cd9YT .node ellipse,#mermaid-svg-9NVejWnLTa6Cd9YT .node polygon,#mermaid-svg-9NVejWnLTa6Cd9YT .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9NVejWnLTa6Cd9YT .rough-node .label text,#mermaid-svg-9NVejWnLTa6Cd9YT .node .label text,#mermaid-svg-9NVejWnLTa6Cd9YT .image-shape .label,#mermaid-svg-9NVejWnLTa6Cd9YT .icon-shape .label{text-anchor:middle;}#mermaid-svg-9NVejWnLTa6Cd9YT .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9NVejWnLTa6Cd9YT .rough-node .label,#mermaid-svg-9NVejWnLTa6Cd9YT .node .label,#mermaid-svg-9NVejWnLTa6Cd9YT .image-shape .label,#mermaid-svg-9NVejWnLTa6Cd9YT .icon-shape .label{text-align:center;}#mermaid-svg-9NVejWnLTa6Cd9YT .node.clickable{cursor:pointer;}#mermaid-svg-9NVejWnLTa6Cd9YT .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9NVejWnLTa6Cd9YT .arrowheadPath{fill:#333333;}#mermaid-svg-9NVejWnLTa6Cd9YT .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9NVejWnLTa6Cd9YT .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9NVejWnLTa6Cd9YT .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9NVejWnLTa6Cd9YT .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9NVejWnLTa6Cd9YT .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9NVejWnLTa6Cd9YT .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster text{fill:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT .cluster span{color:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT 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-9NVejWnLTa6Cd9YT .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9NVejWnLTa6Cd9YT rect.text{fill:none;stroke-width:0;}#mermaid-svg-9NVejWnLTa6Cd9YT .icon-shape,#mermaid-svg-9NVejWnLTa6Cd9YT .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9NVejWnLTa6Cd9YT .icon-shape p,#mermaid-svg-9NVejWnLTa6Cd9YT .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9NVejWnLTa6Cd9YT .icon-shape .label rect,#mermaid-svg-9NVejWnLTa6Cd9YT .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9NVejWnLTa6Cd9YT .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9NVejWnLTa6Cd9YT .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9NVejWnLTa6Cd9YT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



infinite
有限
rerunTimeout
SIGHUP
其他信号
run 开始
设置 defer: 清理输出文件
创建 TimestampLabeler
创建 LoopLabelers

从 manager+vgpu+config
合并 Labelers
生成 Labels
labels 数量 <= 1?
警告: 无标签生成
输出标签
oneshot 模式?
返回 false 退出
sleep interval?
永久休眠
设置 rerunTimeout
等待信号
信号类型
返回 true 触发重启
返回 false 退出

逐行解析 run()

go 复制代码
func (d *gfd) run(sigs chan os.Signal) (bool, error) {
    // defer 逻辑:在退出时清理输出文件(仅在非 oneshot、非 infinite、有输出文件路径时)
    defer func() {
        if d.config.Flags.UseNodeFeatureAPI != nil && *d.config.Flags.UseNodeFeatureAPI {
            return  // 使用 NFD API 时不清理文件
        }
        if d.config.Flags.GFD.Oneshot != nil && *d.config.Flags.GFD.Oneshot {
            return  // oneshot 模式不清理
        }
        if d.config.Flags.GFD.SleepInterval.IsInfinite() {
            return  // 无限休眠不清理
        }
        if d.config.Flags.GFD.OutputFile != nil && *d.config.Flags.GFD.OutputFile == "" {
            return  // 无输出文件不清理
        }
        err := removeOutputFile(*d.config.Flags.GFD.OutputFile)
        if err != nil {
            klog.Warningf("Error removing output file: %v", err)
        }
    }()

    timestampLabeler := lm.NewTimestampLabeler(d.config)  // 时间戳标签生成器

rerun:  // 标签重新生成入口(goto 目标)
    loopLabelers, err := lm.NewLabelers(d.manager, d.vgpu, d.config)  // 核心标签生成器集合
    if err != nil {
        return false, err
    }

    // 合并时间戳标签和业务标签
    labelers := lm.Merge(timestampLabeler, loopLabelers)

    labels, err := labelers.Labels()  // 执行标签生成
    if err != nil {
        return false, fmt.Errorf("error generating labels: %v", err)
    }

    if len(labels) <= 1 {  // 只有时间戳标签 → 无业务标签
        klog.Warning("No labels generated from any source")
    }

    klog.Info("Creating Labels")
    if err := d.labelOutputer.Output(labels); err != nil {  // 输出到文件或 API
        return false, err
    }

    if *d.config.Flags.GFD.Oneshot {
        return false, nil  // oneshot 模式:标注一次后退出
    }

    // 设置下次重跑的定时器
    var rerunTimeout <-chan time.Time
    if d.config.Flags.GFD.SleepInterval.IsInfinite() {
        klog.Info("Sleep interval is infinite, sleeping indefinitely")
    } else {
        klog.Info("Sleeping for ", *d.config.Flags.GFD.SleepInterval)
        rerunTimeout = time.After(time.Duration(*d.config.Flags.GFD.SleepInterval))
    }

    for {
        select {
        case <-rerunTimeout:
            goto rerun  // 定时器到期,重新标注
        case s := <-sigs:
            switch s {
            case syscall.SIGHUP:
                klog.Info("Received SIGHUP, restarting.")
                return true, nil  // 通知外层循环重新加载配置
            default:
                klog.Infof("Received signal %v, shutting down.", s)
                return false, nil  // 优雅退出
            }
        }
    }
}
2.5.4 removeOutputFile() --- 清理函数

清理临时目录和输出文件,转为绝对路径后执行 RemoveAllRemove

2.6 GFD 架构图

#mermaid-svg-DVWl0s3AV9Jczlps{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-DVWl0s3AV9Jczlps .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DVWl0s3AV9Jczlps .error-icon{fill:#552222;}#mermaid-svg-DVWl0s3AV9Jczlps .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DVWl0s3AV9Jczlps .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DVWl0s3AV9Jczlps .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DVWl0s3AV9Jczlps .marker.cross{stroke:#333333;}#mermaid-svg-DVWl0s3AV9Jczlps svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DVWl0s3AV9Jczlps p{margin:0;}#mermaid-svg-DVWl0s3AV9Jczlps .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DVWl0s3AV9Jczlps .cluster-label text{fill:#333;}#mermaid-svg-DVWl0s3AV9Jczlps .cluster-label span{color:#333;}#mermaid-svg-DVWl0s3AV9Jczlps .cluster-label span p{background-color:transparent;}#mermaid-svg-DVWl0s3AV9Jczlps .label text,#mermaid-svg-DVWl0s3AV9Jczlps span{fill:#333;color:#333;}#mermaid-svg-DVWl0s3AV9Jczlps .node rect,#mermaid-svg-DVWl0s3AV9Jczlps .node circle,#mermaid-svg-DVWl0s3AV9Jczlps .node ellipse,#mermaid-svg-DVWl0s3AV9Jczlps .node polygon,#mermaid-svg-DVWl0s3AV9Jczlps .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DVWl0s3AV9Jczlps .rough-node .label text,#mermaid-svg-DVWl0s3AV9Jczlps .node .label text,#mermaid-svg-DVWl0s3AV9Jczlps .image-shape .label,#mermaid-svg-DVWl0s3AV9Jczlps .icon-shape .label{text-anchor:middle;}#mermaid-svg-DVWl0s3AV9Jczlps .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DVWl0s3AV9Jczlps .rough-node .label,#mermaid-svg-DVWl0s3AV9Jczlps .node .label,#mermaid-svg-DVWl0s3AV9Jczlps .image-shape .label,#mermaid-svg-DVWl0s3AV9Jczlps .icon-shape .label{text-align:center;}#mermaid-svg-DVWl0s3AV9Jczlps .node.clickable{cursor:pointer;}#mermaid-svg-DVWl0s3AV9Jczlps .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DVWl0s3AV9Jczlps .arrowheadPath{fill:#333333;}#mermaid-svg-DVWl0s3AV9Jczlps .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DVWl0s3AV9Jczlps .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DVWl0s3AV9Jczlps .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DVWl0s3AV9Jczlps .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DVWl0s3AV9Jczlps .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DVWl0s3AV9Jczlps .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DVWl0s3AV9Jczlps .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DVWl0s3AV9Jczlps .cluster text{fill:#333;}#mermaid-svg-DVWl0s3AV9Jczlps .cluster span{color:#333;}#mermaid-svg-DVWl0s3AV9Jczlps 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-DVWl0s3AV9Jczlps .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DVWl0s3AV9Jczlps rect.text{fill:none;stroke-width:0;}#mermaid-svg-DVWl0s3AV9Jczlps .icon-shape,#mermaid-svg-DVWl0s3AV9Jczlps .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DVWl0s3AV9Jczlps .icon-shape p,#mermaid-svg-DVWl0s3AV9Jczlps .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DVWl0s3AV9Jczlps .icon-shape .label rect,#mermaid-svg-DVWl0s3AV9Jczlps .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DVWl0s3AV9Jczlps .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DVWl0s3AV9Jczlps .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DVWl0s3AV9Jczlps :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 外部
GFD 进程
run 循环
CLI Flags 解析
Config 加载
NVML/Device/Info 库初始化
ResourceManager
vGPU Lib
LabelOutputer
TimestampLabeler
LoopLabelers

GPU/MIG/vGPU/Driver 标签
Merged Labelers
Output

→ 文件 或 NFD API
/etc/kubernetes/.../gfd
NodeFeature CRD
Kubernetes API


3. Config Manager

3.1 模块定位

Config Manager 是一个 动态配置管理工具。它监听 Kubernetes Node 对象上特定标签的变化,根据标签值选择对应的配置文件,通过符号链接将其设为设备插件的实际配置文件,并发送 SIGHUP 信号通知设备插件重新加载配置。

3.2 文件结构

复制代码
cmd/config-manager/
└── main.go    # 单文件实现,约 310 行

3.3 核心常量

go 复制代码
const (
    ResourceNodes = "nodes"                    // K8s 资源名
    DefaultOneshot = false                     // 默认持续运行
    DefaultSendSignal = true                   // 默认发送信号
    DefaultSignal = int(syscall.SIGHUP)        // 默认 SIGHUP
    DefaultProcessToSignal = "nvidia-device-plugin"  // 信号目标进程
    DefaultConfigLabel = "nvidia.com/device-plugin.config"  // 监听的标签
)

// 三种回退策略
const (
    FallbackStrategyNamedConfig  = "named"    // 查找名为 "default" 的配置
    FallbackStrategySingleConfig = "single"   // 只有一个配置时自动选用
    FallbackStrategyEmptyConfig  = "empty"    // 使用空配置
)

const NamedConfigFallback = "default"  // named 策略查找的配置名

3.4 核心数据结构

Flags
go 复制代码
type Flags struct {
    Oneshot            bool              // 单次运行
    Kubeconfig         string            // kubeconfig 路径
    NodeName           string            // 目标节点名
    NodeLabel          string            // 监听的标签 key
    ConfigFileSrcdir   string            // 配置文件源目录(ConfigMap 挂载点)
    ConfigFileDst      string            // 配置文件目标路径(符号链接)
    DefaultConfig      string            // 默认配置名
    FallbackStrategies cli.StringSlice   // 回退策略列表
    SendSignal         bool              // 是否发信号
    Signal             int               // 信号值
    ProcessToSignal    string            // 信号目标进程名
}
SyncableConfig --- 条件变量同步配置
go 复制代码
// SyncableConfig 使用 sync.Cond 实现"等待配置变更"的阻塞语义
// Get() 阻塞直到 Set() 被调用,实现生产者-消费者模式
type SyncableConfig struct {
    cond     *sync.Cond    // 条件变量
    mutex    sync.Mutex    // 互斥锁
    current  string        // 当前配置值
    lastRead *string       // 上次读取的值(用于去重)
}

SyncableConfig 语义分析:
Main Loop SyncableConfig Watcher Goroutine Main Loop SyncableConfig Watcher Goroutine #mermaid-svg-f7Wid71RHsDOHV54{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-f7Wid71RHsDOHV54 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f7Wid71RHsDOHV54 .error-icon{fill:#552222;}#mermaid-svg-f7Wid71RHsDOHV54 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f7Wid71RHsDOHV54 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f7Wid71RHsDOHV54 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f7Wid71RHsDOHV54 .marker.cross{stroke:#333333;}#mermaid-svg-f7Wid71RHsDOHV54 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f7Wid71RHsDOHV54 p{margin:0;}#mermaid-svg-f7Wid71RHsDOHV54 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-f7Wid71RHsDOHV54 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-f7Wid71RHsDOHV54 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-f7Wid71RHsDOHV54 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-f7Wid71RHsDOHV54 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-f7Wid71RHsDOHV54 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-f7Wid71RHsDOHV54 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-f7Wid71RHsDOHV54 .sequenceNumber{fill:white;}#mermaid-svg-f7Wid71RHsDOHV54 #sequencenumber{fill:#333;}#mermaid-svg-f7Wid71RHsDOHV54 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-f7Wid71RHsDOHV54 .messageText{fill:#333;stroke:none;}#mermaid-svg-f7Wid71RHsDOHV54 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-f7Wid71RHsDOHV54 .labelText,#mermaid-svg-f7Wid71RHsDOHV54 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-f7Wid71RHsDOHV54 .loopText,#mermaid-svg-f7Wid71RHsDOHV54 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-f7Wid71RHsDOHV54 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-f7Wid71RHsDOHV54 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-f7Wid71RHsDOHV54 .noteText,#mermaid-svg-f7Wid71RHsDOHV54 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-f7Wid71RHsDOHV54 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-f7Wid71RHsDOHV54 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-f7Wid71RHsDOHV54 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-f7Wid71RHsDOHV54 .actorPopupMenu{position:absolute;}#mermaid-svg-f7Wid71RHsDOHV54 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-f7Wid71RHsDOHV54 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-f7Wid71RHsDOHV54 .actor-man circle,#mermaid-svg-f7Wid71RHsDOHV54 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-f7Wid71RHsDOHV54 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} current = "config-a", broadcast 阻塞(lastRead == current) current = "config-b", broadcast Set("config-a") Get() "config-a" (lastRead = "config-a") Get() Set("config-b") "config-b" (lastRead = "config-b")

关键方法解析:

go 复制代码
// Set 设置新值并唤醒所有等待者
func (m *SyncableConfig) Set(value string) {
    m.mutex.Lock()
    defer m.mutex.Unlock()
    m.current = value
    m.cond.Broadcast()  // 唤醒所有 Get() 等待者
}

// Get 阻塞直到值发生变化
func (m *SyncableConfig) Get() string {
    m.mutex.Lock()
    defer m.mutex.Unlock()
    if m.lastRead != nil && *m.lastRead == m.current {
        m.cond.Wait()  // 值未变,阻塞等待
    }
    val := m.current
    m.lastRead = &val  // 记录已读值
    return *m.lastRead
}

注意lastRead*string 指针类型。每次 Get() 返回时 lastRead 指向新分配的 val 的地址。这种设计避免了 lastReadcurrent 引用同一底层字符串的隐患。

3.5 执行流程深度解析

3.5.1 main() 与 validateFlags()
go 复制代码
func main() {
    flags := Flags{}
    c := cli.NewApp()
    c.Before = func(c *cli.Context) error {
        return validateFlags(c, &flags)  // 在 Action 前校验
    }
    c.Action = func(c *cli.Context) error {
        return start(c, &flags)
    }
    // 定义 11 个 CLI flags(见 3.4)
    c.Flags = []cli.Flag{ ... }
    err := c.Run(os.Args)
    if err != nil { klog.Error(err); os.Exit(1) }
}

// 校验四个必填字段
func validateFlags(c *cli.Context, f *Flags) error {
    if f.NodeName == ""         { return error("invalid <node-name>") }
    if f.NodeLabel == ""        { return error("invalid <node-label>") }
    if f.ConfigFileSrcdir == "" { return error("invalid <config-file-srcdir>") }
    if f.ConfigFileDst == ""    { return error("invalid <config-file-dst>") }
    return nil
}
3.5.2 start() --- 主流程

#mermaid-svg-jQUUWGsT3soEfyCj{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-jQUUWGsT3soEfyCj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jQUUWGsT3soEfyCj .error-icon{fill:#552222;}#mermaid-svg-jQUUWGsT3soEfyCj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jQUUWGsT3soEfyCj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jQUUWGsT3soEfyCj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jQUUWGsT3soEfyCj .marker.cross{stroke:#333333;}#mermaid-svg-jQUUWGsT3soEfyCj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jQUUWGsT3soEfyCj p{margin:0;}#mermaid-svg-jQUUWGsT3soEfyCj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jQUUWGsT3soEfyCj .cluster-label text{fill:#333;}#mermaid-svg-jQUUWGsT3soEfyCj .cluster-label span{color:#333;}#mermaid-svg-jQUUWGsT3soEfyCj .cluster-label span p{background-color:transparent;}#mermaid-svg-jQUUWGsT3soEfyCj .label text,#mermaid-svg-jQUUWGsT3soEfyCj span{fill:#333;color:#333;}#mermaid-svg-jQUUWGsT3soEfyCj .node rect,#mermaid-svg-jQUUWGsT3soEfyCj .node circle,#mermaid-svg-jQUUWGsT3soEfyCj .node ellipse,#mermaid-svg-jQUUWGsT3soEfyCj .node polygon,#mermaid-svg-jQUUWGsT3soEfyCj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jQUUWGsT3soEfyCj .rough-node .label text,#mermaid-svg-jQUUWGsT3soEfyCj .node .label text,#mermaid-svg-jQUUWGsT3soEfyCj .image-shape .label,#mermaid-svg-jQUUWGsT3soEfyCj .icon-shape .label{text-anchor:middle;}#mermaid-svg-jQUUWGsT3soEfyCj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jQUUWGsT3soEfyCj .rough-node .label,#mermaid-svg-jQUUWGsT3soEfyCj .node .label,#mermaid-svg-jQUUWGsT3soEfyCj .image-shape .label,#mermaid-svg-jQUUWGsT3soEfyCj .icon-shape .label{text-align:center;}#mermaid-svg-jQUUWGsT3soEfyCj .node.clickable{cursor:pointer;}#mermaid-svg-jQUUWGsT3soEfyCj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jQUUWGsT3soEfyCj .arrowheadPath{fill:#333333;}#mermaid-svg-jQUUWGsT3soEfyCj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jQUUWGsT3soEfyCj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jQUUWGsT3soEfyCj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jQUUWGsT3soEfyCj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jQUUWGsT3soEfyCj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jQUUWGsT3soEfyCj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jQUUWGsT3soEfyCj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jQUUWGsT3soEfyCj .cluster text{fill:#333;}#mermaid-svg-jQUUWGsT3soEfyCj .cluster span{color:#333;}#mermaid-svg-jQUUWGsT3soEfyCj 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-jQUUWGsT3soEfyCj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jQUUWGsT3soEfyCj rect.text{fill:none;stroke-width:0;}#mermaid-svg-jQUUWGsT3soEfyCj .icon-shape,#mermaid-svg-jQUUWGsT3soEfyCj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jQUUWGsT3soEfyCj .icon-shape p,#mermaid-svg-jQUUWGsT3soEfyCj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jQUUWGsT3soEfyCj .icon-shape .label rect,#mermaid-svg-jQUUWGsT3soEfyCj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jQUUWGsT3soEfyCj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jQUUWGsT3soEfyCj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jQUUWGsT3soEfyCj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

start
构建 K8s 客户端配置
创建 Clientset
创建 SyncableConfig
启动 Informer 协程

持续监听 Node 标签
阻塞等待配置变更

config.Get
收到新配置值
updateConfig
oneshot 或 error?
返回

go 复制代码
func start(c *cli.Context, f *Flags) error {
    // 1. 构建 K8s 客户端
    kubeconfig, err := clientcmd.BuildConfigFromFlags("", f.Kubeconfig)
    if err != nil { return ... }
    clientset, err := kubernetes.NewForConfig(kubeconfig)
    if err != nil { return ... }

    // 2. 创建同步配置对象
    config := NewSyncableConfig(f)

    // 3. 启动 Informer 后台协程,持续监听 Node 标签变化
    stop := continuouslySyncConfigChanges(clientset, config, f)
    defer close(stop)  // 退出时停止 Informer

    // 4. 主循环:阻塞等待配置变更
    for {
        klog.Infof("Waiting for change to '%s' label", f.NodeLabel)
        config := config.Get()  // 阻塞直到标签变化
        klog.Infof("Label change detected: %s=%s", f.NodeLabel, config)
        err := updateConfig(config, f)  // 应用配置变更
        if f.Oneshot || err != nil {
            return err  // oneshot 模式或有错误时退出
        }
    }
}
3.5.3 continuouslySyncConfigChanges() --- Node Informer

#mermaid-svg-yGFNudjNNB4C2t9w{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-yGFNudjNNB4C2t9w .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-yGFNudjNNB4C2t9w .error-icon{fill:#552222;}#mermaid-svg-yGFNudjNNB4C2t9w .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-yGFNudjNNB4C2t9w .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-yGFNudjNNB4C2t9w .marker{fill:#333333;stroke:#333333;}#mermaid-svg-yGFNudjNNB4C2t9w .marker.cross{stroke:#333333;}#mermaid-svg-yGFNudjNNB4C2t9w svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-yGFNudjNNB4C2t9w p{margin:0;}#mermaid-svg-yGFNudjNNB4C2t9w .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-yGFNudjNNB4C2t9w .cluster-label text{fill:#333;}#mermaid-svg-yGFNudjNNB4C2t9w .cluster-label span{color:#333;}#mermaid-svg-yGFNudjNNB4C2t9w .cluster-label span p{background-color:transparent;}#mermaid-svg-yGFNudjNNB4C2t9w .label text,#mermaid-svg-yGFNudjNNB4C2t9w span{fill:#333;color:#333;}#mermaid-svg-yGFNudjNNB4C2t9w .node rect,#mermaid-svg-yGFNudjNNB4C2t9w .node circle,#mermaid-svg-yGFNudjNNB4C2t9w .node ellipse,#mermaid-svg-yGFNudjNNB4C2t9w .node polygon,#mermaid-svg-yGFNudjNNB4C2t9w .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-yGFNudjNNB4C2t9w .rough-node .label text,#mermaid-svg-yGFNudjNNB4C2t9w .node .label text,#mermaid-svg-yGFNudjNNB4C2t9w .image-shape .label,#mermaid-svg-yGFNudjNNB4C2t9w .icon-shape .label{text-anchor:middle;}#mermaid-svg-yGFNudjNNB4C2t9w .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-yGFNudjNNB4C2t9w .rough-node .label,#mermaid-svg-yGFNudjNNB4C2t9w .node .label,#mermaid-svg-yGFNudjNNB4C2t9w .image-shape .label,#mermaid-svg-yGFNudjNNB4C2t9w .icon-shape .label{text-align:center;}#mermaid-svg-yGFNudjNNB4C2t9w .node.clickable{cursor:pointer;}#mermaid-svg-yGFNudjNNB4C2t9w .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-yGFNudjNNB4C2t9w .arrowheadPath{fill:#333333;}#mermaid-svg-yGFNudjNNB4C2t9w .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-yGFNudjNNB4C2t9w .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-yGFNudjNNB4C2t9w .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yGFNudjNNB4C2t9w .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-yGFNudjNNB4C2t9w .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yGFNudjNNB4C2t9w .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-yGFNudjNNB4C2t9w .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-yGFNudjNNB4C2t9w .cluster text{fill:#333;}#mermaid-svg-yGFNudjNNB4C2t9w .cluster span{color:#333;}#mermaid-svg-yGFNudjNNB4C2t9w 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-yGFNudjNNB4C2t9w .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-yGFNudjNNB4C2t9w rect.text{fill:none;stroke-width:0;}#mermaid-svg-yGFNudjNNB4C2t9w .icon-shape,#mermaid-svg-yGFNudjNNB4C2t9w .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-yGFNudjNNB4C2t9w .icon-shape p,#mermaid-svg-yGFNudjNNB4C2t9w .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-yGFNudjNNB4C2t9w .icon-shape .label rect,#mermaid-svg-yGFNudjNNB4C2t9w .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-yGFNudjNNB4C2t9w .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-yGFNudjNNB4C2t9w .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-yGFNudjNNB4C2t9w :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Add
Update


Delete


创建 ListWatch

过滤当前 Node
创建 Informer
启动 Informer 协程
事件类型
config.Set labelsnodeLabel
标签值变化?
config.Set newLabel
旧标签非空?
config.Set ''

go 复制代码
func continuouslySyncConfigChanges(clientset *kubernetes.Clientset, config *SyncableConfig, f *Flags) chan struct{} {
    // 创建 ListWatch:仅关注当前节点
    listWatch := cache.NewListWatchFromClient(
        clientset.CoreV1().RESTClient(),
        ResourceNodes,                          // "nodes"
        v1.NamespaceAll,
        fields.OneTermEqualSelector("metadata.name", f.NodeName),  // 按节点名过滤
    )

    // 创建 Informer,注册三种事件处理器
    _, controller := cache.NewInformerWithOptions(
        cache.InformerOptions{
            ListerWatcher: listWatch,
            ObjectType:    &v1.Node{},
            Handler: cache.ResourceEventHandlerFuncs{
                AddFunc: func(obj interface{}) {
                    // 节点新增:直接设置当前标签值
                    config.Set(obj.(*v1.Node).Labels[f.NodeLabel])
                },
                UpdateFunc: func(oldObj, newObj interface{}) {
                    // 节点更新:仅在标签值变化时通知
                    oldLabel := oldObj.(*v1.Node).Labels[f.NodeLabel]
                    newLabel := newObj.(*v1.Node).Labels[f.NodeLabel]
                    if oldLabel != newLabel {
                        config.Set(newLabel)
                    }
                },
                DeleteFunc: func(obj interface{}) {
                    // 节点删除:清空配置
                    oldLabel := obj.(*v1.Node).Labels[f.NodeLabel]
                    if oldLabel != "" {
                        config.Set("")
                    }
                },
            },
            ResyncPeriod: 0,  // 不做定期重同步
        },
    )

    stop := make(chan struct{})
    go controller.Run(stop)  // 后台运行
    return stop
}
3.5.4 updateConfig() --- 配置应用

#mermaid-svg-vFZxyivnjlsTfJWH{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-vFZxyivnjlsTfJWH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vFZxyivnjlsTfJWH .error-icon{fill:#552222;}#mermaid-svg-vFZxyivnjlsTfJWH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vFZxyivnjlsTfJWH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vFZxyivnjlsTfJWH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vFZxyivnjlsTfJWH .marker.cross{stroke:#333333;}#mermaid-svg-vFZxyivnjlsTfJWH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vFZxyivnjlsTfJWH p{margin:0;}#mermaid-svg-vFZxyivnjlsTfJWH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vFZxyivnjlsTfJWH .cluster-label text{fill:#333;}#mermaid-svg-vFZxyivnjlsTfJWH .cluster-label span{color:#333;}#mermaid-svg-vFZxyivnjlsTfJWH .cluster-label span p{background-color:transparent;}#mermaid-svg-vFZxyivnjlsTfJWH .label text,#mermaid-svg-vFZxyivnjlsTfJWH span{fill:#333;color:#333;}#mermaid-svg-vFZxyivnjlsTfJWH .node rect,#mermaid-svg-vFZxyivnjlsTfJWH .node circle,#mermaid-svg-vFZxyivnjlsTfJWH .node ellipse,#mermaid-svg-vFZxyivnjlsTfJWH .node polygon,#mermaid-svg-vFZxyivnjlsTfJWH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vFZxyivnjlsTfJWH .rough-node .label text,#mermaid-svg-vFZxyivnjlsTfJWH .node .label text,#mermaid-svg-vFZxyivnjlsTfJWH .image-shape .label,#mermaid-svg-vFZxyivnjlsTfJWH .icon-shape .label{text-anchor:middle;}#mermaid-svg-vFZxyivnjlsTfJWH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vFZxyivnjlsTfJWH .rough-node .label,#mermaid-svg-vFZxyivnjlsTfJWH .node .label,#mermaid-svg-vFZxyivnjlsTfJWH .image-shape .label,#mermaid-svg-vFZxyivnjlsTfJWH .icon-shape .label{text-align:center;}#mermaid-svg-vFZxyivnjlsTfJWH .node.clickable{cursor:pointer;}#mermaid-svg-vFZxyivnjlsTfJWH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vFZxyivnjlsTfJWH .arrowheadPath{fill:#333333;}#mermaid-svg-vFZxyivnjlsTfJWH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vFZxyivnjlsTfJWH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vFZxyivnjlsTfJWH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vFZxyivnjlsTfJWH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vFZxyivnjlsTfJWH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vFZxyivnjlsTfJWH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vFZxyivnjlsTfJWH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vFZxyivnjlsTfJWH .cluster text{fill:#333;}#mermaid-svg-vFZxyivnjlsTfJWH .cluster span{color:#333;}#mermaid-svg-vFZxyivnjlsTfJWH 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-vFZxyivnjlsTfJWH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vFZxyivnjlsTfJWH rect.text{fill:none;stroke-width:0;}#mermaid-svg-vFZxyivnjlsTfJWH .icon-shape,#mermaid-svg-vFZxyivnjlsTfJWH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vFZxyivnjlsTfJWH .icon-shape p,#mermaid-svg-vFZxyivnjlsTfJWH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vFZxyivnjlsTfJWH .icon-shape .label rect,#mermaid-svg-vFZxyivnjlsTfJWH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vFZxyivnjlsTfJWH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vFZxyivnjlsTfJWH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vFZxyivnjlsTfJWH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是





updateConfig
updateConfigName

解析配置名
配置名为空?
日志: 空配置
日志: 配置名
updateSymlink

更新符号链接
符号链接已更新?
跳过:已是目标配置
send-signal?
完成
signalProcess

发送 SIGHUP

3.5.5 updateConfigName() --- 配置名解析与回退
go 复制代码
func updateConfigName(config string, f *Flags) (string, error) {
    // 1. 获取可用配置文件列表
    files, err := getConfigFileNameMap(f)
    if err != nil { return "", err }
    if len(files) == 0 { return "", error("no configuration files") }

    // 2. 如果有显式配置名,检查是否存在
    if config != "" {
        if !files[config] { return "", error("specified config does not exist") }
        return config, nil
    }

    // 3. 如果有默认配置,检查是否存在
    if f.DefaultConfig != "" {
        if !files[f.DefaultConfig] { return "", error("default config does not exist") }
        return f.DefaultConfig, nil
    }

    // 4. 按顺序尝试回退策略
    for _, fallback := range f.FallbackStrategies.Value() {
        switch fallback {
        case "named":   // 查找名为 "default" 的配置
            if files[NamedConfigFallback] { return NamedConfigFallback, nil }
        case "single":  // 只有一个配置时自动选用
            if len(files) == 1 { return filenames[0], nil }
        case "empty":   // 使用空配置
            return "", nil
        }
    }
    return "", error("no config set, no default, all fallbacks failed")
}

回退策略流程图:
#mermaid-svg-HbzGU1CyAni3oXgj{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-HbzGU1CyAni3oXgj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-HbzGU1CyAni3oXgj .error-icon{fill:#552222;}#mermaid-svg-HbzGU1CyAni3oXgj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-HbzGU1CyAni3oXgj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-HbzGU1CyAni3oXgj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-HbzGU1CyAni3oXgj .marker.cross{stroke:#333333;}#mermaid-svg-HbzGU1CyAni3oXgj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-HbzGU1CyAni3oXgj p{margin:0;}#mermaid-svg-HbzGU1CyAni3oXgj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-HbzGU1CyAni3oXgj .cluster-label text{fill:#333;}#mermaid-svg-HbzGU1CyAni3oXgj .cluster-label span{color:#333;}#mermaid-svg-HbzGU1CyAni3oXgj .cluster-label span p{background-color:transparent;}#mermaid-svg-HbzGU1CyAni3oXgj .label text,#mermaid-svg-HbzGU1CyAni3oXgj span{fill:#333;color:#333;}#mermaid-svg-HbzGU1CyAni3oXgj .node rect,#mermaid-svg-HbzGU1CyAni3oXgj .node circle,#mermaid-svg-HbzGU1CyAni3oXgj .node ellipse,#mermaid-svg-HbzGU1CyAni3oXgj .node polygon,#mermaid-svg-HbzGU1CyAni3oXgj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-HbzGU1CyAni3oXgj .rough-node .label text,#mermaid-svg-HbzGU1CyAni3oXgj .node .label text,#mermaid-svg-HbzGU1CyAni3oXgj .image-shape .label,#mermaid-svg-HbzGU1CyAni3oXgj .icon-shape .label{text-anchor:middle;}#mermaid-svg-HbzGU1CyAni3oXgj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-HbzGU1CyAni3oXgj .rough-node .label,#mermaid-svg-HbzGU1CyAni3oXgj .node .label,#mermaid-svg-HbzGU1CyAni3oXgj .image-shape .label,#mermaid-svg-HbzGU1CyAni3oXgj .icon-shape .label{text-align:center;}#mermaid-svg-HbzGU1CyAni3oXgj .node.clickable{cursor:pointer;}#mermaid-svg-HbzGU1CyAni3oXgj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-HbzGU1CyAni3oXgj .arrowheadPath{fill:#333333;}#mermaid-svg-HbzGU1CyAni3oXgj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-HbzGU1CyAni3oXgj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-HbzGU1CyAni3oXgj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HbzGU1CyAni3oXgj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-HbzGU1CyAni3oXgj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HbzGU1CyAni3oXgj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-HbzGU1CyAni3oXgj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-HbzGU1CyAni3oXgj .cluster text{fill:#333;}#mermaid-svg-HbzGU1CyAni3oXgj .cluster span{color:#333;}#mermaid-svg-HbzGU1CyAni3oXgj 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-HbzGU1CyAni3oXgj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-HbzGU1CyAni3oXgj rect.text{fill:none;stroke-width:0;}#mermaid-svg-HbzGU1CyAni3oXgj .icon-shape,#mermaid-svg-HbzGU1CyAni3oXgj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-HbzGU1CyAni3oXgj .icon-shape p,#mermaid-svg-HbzGU1CyAni3oXgj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-HbzGU1CyAni3oXgj .icon-shape .label rect,#mermaid-svg-HbzGU1CyAni3oXgj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-HbzGU1CyAni3oXgj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-HbzGU1CyAni3oXgj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-HbzGU1CyAni3oXgj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是













config 值为空
有 DefaultConfig?
DefaultConfig 存在?
返回 DefaultConfig
报错
遍历 FallbackStrategies
策略: named?
存在 'default' 配置?
返回 'default'
继续下一策略
策略: single?
只有一个配置?
返回该配置
策略: empty?
返回空字符串
报错: 未知策略

3.5.6 updateSymlink() --- 符号链接管理
go 复制代码
func updateSymlink(config string, f *Flags) (bool, error) {
    src := "/dev/null"                          // 空配置指向 /dev/null
    if config != "" {
        src = filepath.Join(f.ConfigFileSrcdir, config)  // 非空指向源目录下的配置文件
    }

    // 检查目标是否已存在
    exists, err := fileExists(f.ConfigFileDst)
    if exists {
        // 比较现有符号链接目标与新目标
        srcRealpath, _ := filepath.EvalSymlinks(src)
        dstRealpath, _ := filepath.EvalSymlinks(f.ConfigFileDst)
        if srcRealpath == dstRealpath {
            return false, nil  // 已指向正确目标,无需更新
        }
        // 删除旧符号链接
        os.Remove(f.ConfigFileDst)
    }

    // 创建新符号链接
    os.Symlink(src, f.ConfigFileDst)
    return true, nil
}

3.5.7 signalProcess() --- 信号发送

通过 procfs.AllProcs() 遍历 /proc 下所有进程,匹配 cmdline[0] == f.ProcessToSignal,找到 PID 后 syscall.Kill(pid, signal) 发送信号。

3.5.8 getConfigFileNameMap() --- 配置文件发现

读取源目录,排除子目录和 ConfigMap 特殊文件(以 .. 开头的元数据文件如 ..data..2025_07_26_..),返回可用配置名 map。

3.6 Config Manager 完整流程图

nvidia-device-plugin Filesystem Main Loop SyncableConfig Informer Kubernetes API nvidia-device-plugin Filesystem Main Loop SyncableConfig Informer Kubernetes API #mermaid-svg-inNQdjIVNmqi8pgS{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-inNQdjIVNmqi8pgS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-inNQdjIVNmqi8pgS .error-icon{fill:#552222;}#mermaid-svg-inNQdjIVNmqi8pgS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-inNQdjIVNmqi8pgS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-inNQdjIVNmqi8pgS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-inNQdjIVNmqi8pgS .marker.cross{stroke:#333333;}#mermaid-svg-inNQdjIVNmqi8pgS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-inNQdjIVNmqi8pgS p{margin:0;}#mermaid-svg-inNQdjIVNmqi8pgS .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-inNQdjIVNmqi8pgS text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-inNQdjIVNmqi8pgS .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-inNQdjIVNmqi8pgS .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-inNQdjIVNmqi8pgS .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-inNQdjIVNmqi8pgS .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-inNQdjIVNmqi8pgS #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-inNQdjIVNmqi8pgS .sequenceNumber{fill:white;}#mermaid-svg-inNQdjIVNmqi8pgS #sequencenumber{fill:#333;}#mermaid-svg-inNQdjIVNmqi8pgS #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-inNQdjIVNmqi8pgS .messageText{fill:#333;stroke:none;}#mermaid-svg-inNQdjIVNmqi8pgS .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-inNQdjIVNmqi8pgS .labelText,#mermaid-svg-inNQdjIVNmqi8pgS .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-inNQdjIVNmqi8pgS .loopText,#mermaid-svg-inNQdjIVNmqi8pgS .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-inNQdjIVNmqi8pgS .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-inNQdjIVNmqi8pgS .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-inNQdjIVNmqi8pgS .noteText,#mermaid-svg-inNQdjIVNmqi8pgS .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-inNQdjIVNmqi8pgS .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-inNQdjIVNmqi8pgS .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-inNQdjIVNmqi8pgS .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-inNQdjIVNmqi8pgS .actorPopupMenu{position:absolute;}#mermaid-svg-inNQdjIVNmqi8pgS .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-inNQdjIVNmqi8pgS .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-inNQdjIVNmqi8pgS .actor-man circle,#mermaid-svg-inNQdjIVNmqi8pgS line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-inNQdjIVNmqi8pgS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Node label 变更事件 Set("new-config") Get() 返回 "new-config" updateConfigName("new-config") 读取源目录,验证配置存在 EvalSymlinks 比较当前链接 Remove 旧链接 + Symlink 新链接 findPidToSignal("nvidia-device-plugin") syscall.Kill(pid, SIGHUP) 重新加载配置 Get() 阻塞等待下一次变更


4. MPS Control Daemon

4.1 模块定位

MPS Control Daemon 是 NVIDIA Multi-Process Service 的 Kubernetes 侧管理器。MPS 允许多个进程共享同一块 GPU,通过软硬件协同提供 GPU 隔离。此守护进程负责:

  • 为每个共享资源启动 nvidia-cuda-mps-control 后台进程
  • 设置 GPU 的计算模式为 EXCLUSIVE_PROCESS
  • 配置每设备的显存限制和线程百分比
  • 管理 MPS 管道目录、日志目录和共享内存

4.2 文件结构

复制代码
cmd/mps-control-daemon/
├── main.go                    # 入口,守护进程生命周期管理
├── mps/
│   ├── daemon.go              # MPS Daemon 实现(启停、控制)
│   ├── device.go              # MPS 设备校验(replica 限制)
│   ├── log-tailer.go          # 日志跟踪器
│   ├── manager.go             # Daemon 管理器(创建多个 Daemon)
│   ├── options.go             # 函数式选项模式
│   └── root.go                # 路径管理
└── mount/
    └── mount-shm.go           # tmpfs 共享内存挂载

4.3 入口 main.go 深度解析

4.3.1 main() 函数

创建 cli.App,注册 --config-file--mig-strategy 两个顶层 flag,注册 mount-shm 子命令,c.Action 调用 start()

4.3.2 loadConfig() --- 配置加载

通过 spec.NewConfig() 从 CLI flags + 配置文件加载配置,调用 validateFlags()(当前空实现),清空 config.Flags.GFD = nil(MPS 不需要 GFD 配置)。

4.3.3 start() --- 生命周期管理

#mermaid-svg-3z0YrcsyCmWIE3Eb{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-3z0YrcsyCmWIE3Eb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-3z0YrcsyCmWIE3Eb .error-icon{fill:#552222;}#mermaid-svg-3z0YrcsyCmWIE3Eb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-3z0YrcsyCmWIE3Eb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .marker.cross{stroke:#333333;}#mermaid-svg-3z0YrcsyCmWIE3Eb svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-3z0YrcsyCmWIE3Eb p{margin:0;}#mermaid-svg-3z0YrcsyCmWIE3Eb .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster-label text{fill:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster-label span{color:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster-label span p{background-color:transparent;}#mermaid-svg-3z0YrcsyCmWIE3Eb .label text,#mermaid-svg-3z0YrcsyCmWIE3Eb span{fill:#333;color:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .node rect,#mermaid-svg-3z0YrcsyCmWIE3Eb .node circle,#mermaid-svg-3z0YrcsyCmWIE3Eb .node ellipse,#mermaid-svg-3z0YrcsyCmWIE3Eb .node polygon,#mermaid-svg-3z0YrcsyCmWIE3Eb .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .rough-node .label text,#mermaid-svg-3z0YrcsyCmWIE3Eb .node .label text,#mermaid-svg-3z0YrcsyCmWIE3Eb .image-shape .label,#mermaid-svg-3z0YrcsyCmWIE3Eb .icon-shape .label{text-anchor:middle;}#mermaid-svg-3z0YrcsyCmWIE3Eb .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .rough-node .label,#mermaid-svg-3z0YrcsyCmWIE3Eb .node .label,#mermaid-svg-3z0YrcsyCmWIE3Eb .image-shape .label,#mermaid-svg-3z0YrcsyCmWIE3Eb .icon-shape .label{text-align:center;}#mermaid-svg-3z0YrcsyCmWIE3Eb .node.clickable{cursor:pointer;}#mermaid-svg-3z0YrcsyCmWIE3Eb .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .arrowheadPath{fill:#333333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3z0YrcsyCmWIE3Eb .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-3z0YrcsyCmWIE3Eb .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3z0YrcsyCmWIE3Eb .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster text{fill:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb .cluster span{color:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb 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-3z0YrcsyCmWIE3Eb .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-3z0YrcsyCmWIE3Eb rect.text{fill:none;stroke-width:0;}#mermaid-svg-3z0YrcsyCmWIE3Eb .icon-shape,#mermaid-svg-3z0YrcsyCmWIE3Eb .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-3z0YrcsyCmWIE3Eb .icon-shape p,#mermaid-svg-3z0YrcsyCmWIE3Eb .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-3z0YrcsyCmWIE3Eb .icon-shape .label rect,#mermaid-svg-3z0YrcsyCmWIE3Eb .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-3z0YrcsyCmWIE3Eb .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-3z0YrcsyCmWIE3Eb .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-3z0YrcsyCmWIE3Eb :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 失败
成功
重试超时
SIGHUP
其他信号
start
启动信号监听
startDaemons
启动成功?
设置 30s 重试定时器
创建 /mps/.ready 文件
等待事件
事件类型
stopDaemons 旧实例
stopDaemons
退出

逐行解析:

go 复制代码
func start(c *cli.Context, cfg *Config) error {
    klog.Info("Starting OS watcher.")
    sigs := watch.Signals(syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)

    var started bool                          // 是否已启动过(用于重启时停止旧实例)
    var restartTimeout <-chan time.Time       // 重试定时器
    var daemons []*mps.Daemon                 // 当前运行的 Daemon 列表

restart:
    if started {  // 如果是重启,先停止旧的所有 daemon
        err := stopDaemons(daemons...)
        if err != nil { return ... }
    }

    klog.Info("Starting Daemons.")
    daemons, restartDaemons, err := startDaemons(c, cfg)
    if err != nil { return ... }
    started = true

    if restartDaemons {  // 启动失败,30 秒后重试
        klog.Infof("Failed to start one or more MPS deamons. Retrying in 30s...")
        restartTimeout = time.After(30 * time.Second)
    }

    for {
        select {
        case <-restartTimeout:
            goto restart  // 重试
        case s := <-sigs:
            switch s {
            case syscall.SIGHUP:
                klog.Info("Received SIGHUP, restarting.")
                goto restart  // 重新启动所有 daemon
            default:
                klog.Infof("Received signal \"%v\", shutting down.", s)
                goto exit
            }
        }
    }

exit:
    if err := stopDaemons(daemons...); err != nil { return ... }
    return nil
}
4.3.4 startDaemons() --- 创建并启动所有 Daemon
go 复制代码
func startDaemons(c *cli.Context, cfg *Config) ([]*mps.Daemon, bool, error) {
    // 1. 加载配置
    config, err := cfg.loadConfig(c)
    spec.DisableResourceNamingInConfig(config)

    // 2. 初始化 NVML 库三层
    nvmllib := nvml.New()
    devicelib := device.New(nvmllib)
    infolib := nvinfo.New(...)

    // 3. 添加默认资源配置
    rm.AddDefaultResourcesToConfig(infolib, nvmllib, devicelib, config)

    // 4. 打印配置 JSON
    configJSON, _ := json.MarshalIndent(config, "", "  ")
    klog.Infof("\nRunning with config:\n%v", string(configJSON))

    // 5. 创建 MPS Daemon 集合
    mpsDaemons, err := mps.NewDaemons(infolib, nvmllib, devicelib,
        mps.WithConfig(config),
    )
    if err != nil { return nil, false, ... }

    if len(mpsDaemons) == 0 {
        klog.Info("No devices are configured for MPS sharing; Waiting indefinitely.")
    }

    // 6. 逐个启动 Daemon
    for _, mpsDaemon := range mpsDaemons {
        if err := mpsDaemon.Start(); err != nil {
            klog.Errorf("Failed to start MPS daemon: %v", err)
            return mpsDaemons, true, nil  // 返回 restartDaemons=true 触发重试
        }
    }

    // 7. 创建 ready 文件,通知外部 daemon 已就绪
    readyFile, err := os.Create("/mps/.ready")
    if err != nil { return mpsDaemons, true, ... }
    defer readyFile.Close()

    return mpsDaemons, false, nil
}
4.3.5 stopDaemons() --- 停止所有 Daemon
go 复制代码
func stopDaemons(mpsDaemons ...*mps.Daemon) error {
    // 1. 删除 ready 文件,通知外部 daemon 已停止
    os.Remove("/mps/.ready")

    // 2. 逐个停止每个 daemon
    klog.Info("Stopping MPS daemons.")
    var errs error
    for _, p := range mpsDaemons {
        errs = errors.Join(errs, p.Stop())  // 累积错误
    }
    return errs
}

4.4 MPS Control Daemon 架构图

#mermaid-svg-gNTeE7STRgo6rpxX{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-gNTeE7STRgo6rpxX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gNTeE7STRgo6rpxX .error-icon{fill:#552222;}#mermaid-svg-gNTeE7STRgo6rpxX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gNTeE7STRgo6rpxX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gNTeE7STRgo6rpxX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gNTeE7STRgo6rpxX .marker.cross{stroke:#333333;}#mermaid-svg-gNTeE7STRgo6rpxX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gNTeE7STRgo6rpxX p{margin:0;}#mermaid-svg-gNTeE7STRgo6rpxX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gNTeE7STRgo6rpxX .cluster-label text{fill:#333;}#mermaid-svg-gNTeE7STRgo6rpxX .cluster-label span{color:#333;}#mermaid-svg-gNTeE7STRgo6rpxX .cluster-label span p{background-color:transparent;}#mermaid-svg-gNTeE7STRgo6rpxX .label text,#mermaid-svg-gNTeE7STRgo6rpxX span{fill:#333;color:#333;}#mermaid-svg-gNTeE7STRgo6rpxX .node rect,#mermaid-svg-gNTeE7STRgo6rpxX .node circle,#mermaid-svg-gNTeE7STRgo6rpxX .node ellipse,#mermaid-svg-gNTeE7STRgo6rpxX .node polygon,#mermaid-svg-gNTeE7STRgo6rpxX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gNTeE7STRgo6rpxX .rough-node .label text,#mermaid-svg-gNTeE7STRgo6rpxX .node .label text,#mermaid-svg-gNTeE7STRgo6rpxX .image-shape .label,#mermaid-svg-gNTeE7STRgo6rpxX .icon-shape .label{text-anchor:middle;}#mermaid-svg-gNTeE7STRgo6rpxX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gNTeE7STRgo6rpxX .rough-node .label,#mermaid-svg-gNTeE7STRgo6rpxX .node .label,#mermaid-svg-gNTeE7STRgo6rpxX .image-shape .label,#mermaid-svg-gNTeE7STRgo6rpxX .icon-shape .label{text-align:center;}#mermaid-svg-gNTeE7STRgo6rpxX .node.clickable{cursor:pointer;}#mermaid-svg-gNTeE7STRgo6rpxX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gNTeE7STRgo6rpxX .arrowheadPath{fill:#333333;}#mermaid-svg-gNTeE7STRgo6rpxX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gNTeE7STRgo6rpxX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gNTeE7STRgo6rpxX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gNTeE7STRgo6rpxX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gNTeE7STRgo6rpxX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gNTeE7STRgo6rpxX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gNTeE7STRgo6rpxX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gNTeE7STRgo6rpxX .cluster text{fill:#333;}#mermaid-svg-gNTeE7STRgo6rpxX .cluster span{color:#333;}#mermaid-svg-gNTeE7STRgo6rpxX 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-gNTeE7STRgo6rpxX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gNTeE7STRgo6rpxX rect.text{fill:none;stroke-width:0;}#mermaid-svg-gNTeE7STRgo6rpxX .icon-shape,#mermaid-svg-gNTeE7STRgo6rpxX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gNTeE7STRgo6rpxX .icon-shape p,#mermaid-svg-gNTeE7STRgo6rpxX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gNTeE7STRgo6rpxX .icon-shape .label rect,#mermaid-svg-gNTeE7STRgo6rpxX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gNTeE7STRgo6rpxX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gNTeE7STRgo6rpxX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gNTeE7STRgo6rpxX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 每个 Daemon 管理
MPS Control Daemon 进程
Per-Resource Daemon
main.go start
startDaemons
mps.Manager
Daemon-1

resource=nvidia.com/gpu
Daemon-2

resource=nvidia.com/gpu.shared
Daemon-N
/mps/.ready 文件
nvidia-cuda-mps-control -d
nvidia-smi -c EXCLUSIVE_PROCESS
/mps/ResourceName/pipe
/mps/ResourceName/log
tail -f control.log


5. MPS Daemon 核心逻辑

5.1 Daemon 结构体

go 复制代码
type Daemon struct {
    rm        rm.ResourceManager    // 关联的资源管理器
    root      Root                  // MPS 根路径(默认 /mps)
    logTailer *tailer               // 日志跟踪器
}

5.2 核心常量与类型

go 复制代码
type computeMode string

const (
    mpsControlBin = "nvidia-cuda-mps-control"  // MPS 控制二进制

    computeModeExclusiveProcess = computeMode("EXCLUSIVE_PROCESS")  // 独占进程模式
    computeModeDefault          = computeMode("DEFAULT")            // 默认模式

    unprivilegedContainerSELinuxLabel = "system_u:object_r:container_file_t:s0"  // SELinux 标签
)

5.3 Start() --- 启动 MPS Daemon

#mermaid-svg-KrvwkeTpld1WFppf{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-KrvwkeTpld1WFppf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KrvwkeTpld1WFppf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KrvwkeTpld1WFppf .error-icon{fill:#552222;}#mermaid-svg-KrvwkeTpld1WFppf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KrvwkeTpld1WFppf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KrvwkeTpld1WFppf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KrvwkeTpld1WFppf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KrvwkeTpld1WFppf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KrvwkeTpld1WFppf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KrvwkeTpld1WFppf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KrvwkeTpld1WFppf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KrvwkeTpld1WFppf .marker.cross{stroke:#333333;}#mermaid-svg-KrvwkeTpld1WFppf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KrvwkeTpld1WFppf p{margin:0;}#mermaid-svg-KrvwkeTpld1WFppf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KrvwkeTpld1WFppf .cluster-label text{fill:#333;}#mermaid-svg-KrvwkeTpld1WFppf .cluster-label span{color:#333;}#mermaid-svg-KrvwkeTpld1WFppf .cluster-label span p{background-color:transparent;}#mermaid-svg-KrvwkeTpld1WFppf .label text,#mermaid-svg-KrvwkeTpld1WFppf span{fill:#333;color:#333;}#mermaid-svg-KrvwkeTpld1WFppf .node rect,#mermaid-svg-KrvwkeTpld1WFppf .node circle,#mermaid-svg-KrvwkeTpld1WFppf .node ellipse,#mermaid-svg-KrvwkeTpld1WFppf .node polygon,#mermaid-svg-KrvwkeTpld1WFppf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KrvwkeTpld1WFppf .rough-node .label text,#mermaid-svg-KrvwkeTpld1WFppf .node .label text,#mermaid-svg-KrvwkeTpld1WFppf .image-shape .label,#mermaid-svg-KrvwkeTpld1WFppf .icon-shape .label{text-anchor:middle;}#mermaid-svg-KrvwkeTpld1WFppf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KrvwkeTpld1WFppf .rough-node .label,#mermaid-svg-KrvwkeTpld1WFppf .node .label,#mermaid-svg-KrvwkeTpld1WFppf .image-shape .label,#mermaid-svg-KrvwkeTpld1WFppf .icon-shape .label{text-align:center;}#mermaid-svg-KrvwkeTpld1WFppf .node.clickable{cursor:pointer;}#mermaid-svg-KrvwkeTpld1WFppf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KrvwkeTpld1WFppf .arrowheadPath{fill:#333333;}#mermaid-svg-KrvwkeTpld1WFppf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KrvwkeTpld1WFppf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KrvwkeTpld1WFppf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KrvwkeTpld1WFppf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KrvwkeTpld1WFppf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KrvwkeTpld1WFppf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KrvwkeTpld1WFppf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KrvwkeTpld1WFppf .cluster text{fill:#333;}#mermaid-svg-KrvwkeTpld1WFppf .cluster span{color:#333;}#mermaid-svg-KrvwkeTpld1WFppf 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-KrvwkeTpld1WFppf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KrvwkeTpld1WFppf rect.text{fill:none;stroke-width:0;}#mermaid-svg-KrvwkeTpld1WFppf .icon-shape,#mermaid-svg-KrvwkeTpld1WFppf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KrvwkeTpld1WFppf .icon-shape p,#mermaid-svg-KrvwkeTpld1WFppf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KrvwkeTpld1WFppf .icon-shape .label rect,#mermaid-svg-KrvwkeTpld1WFppf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KrvwkeTpld1WFppf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KrvwkeTpld1WFppf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KrvwkeTpld1WFppf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

Start
setComputeMode

EXCLUSIVE_PROCESS
创建 PipeDir

/mps/Resource/pipe
设置 SELinux 上下文
创建 LogDir

/mps/Resource/log
启动 nvidia-cuda-mps-control -d
设置每设备显存限制

set_default_device_pinned_mem_limit
有 thread percentage?
设置线程百分比

set_default_active_thread_percentage
创建 .started 文件
启动 log tailer

tail -f control.log
完成

逐行解析 Start():

go 复制代码
func (d *Daemon) Start() error {
    // 1. 将所有关联 GPU 设置为 EXCLUSIVE_PROCESS 模式
    //    MPS 要求 GPU 处于独占进程模式才能正常工作
    if err := d.setComputeMode(computeModeExclusiveProcess); err != nil {
        return fmt.Errorf("error setting compute mode %v: %w", computeModeExclusiveProcess, err)
    }

    klog.InfoS("Staring MPS daemon", "resource", d.rm.Resource())

    // 2. 创建管道目录(MPS 客户端通过此目录与服务端通信)
    pipeDir := d.PipeDir()
    if err := os.MkdirAll(pipeDir, 0755); err != nil {
        return fmt.Errorf("error creating directory %v: %w", pipeDir, err)
    }

    // 3. 设置 SELinux 上下文(确保非特权容器可访问)
    if err := setSELinuxContext(pipeDir, unprivilegedContainerSELinuxLabel); err != nil {
        return fmt.Errorf("error setting SELinux context: %w", err)
    }

    // 4. 创建日志目录
    logDir := d.LogDir()
    if err := os.MkdirAll(logDir, 0755); err != nil {
        return fmt.Errorf("error creating directory %v: %w", logDir, err)
    }

    // 5. 启动 nvidia-cuda-mps-control 守护进程(-d 表示 daemon 模式)
    mpsDaemon := exec.Command(mpsControlBin, "-d")
    mpsDaemon.Env = append(mpsDaemon.Env, d.EnvVars().toSlice()...)  // 设置管道和日志目录环境变量
    if err := mpsDaemon.Run(); err != nil {
        return err
    }

    // 6. 为每个设备设置显存限制
    //    公式:per_device_limit = total_memory / replicas
    for index, limit := range d.perDevicePinnedDeviceMemoryLimits() {
        _, err := d.EchoPipeToControl(
            fmt.Sprintf("set_default_device_pinned_mem_limit %s %s", index, limit),
        )
        if err != nil { return ... }
    }

    // 7. 设置线程百分比(如果有共享副本)
    if threadPercentage := d.activeThreadPercentage(); threadPercentage != "" {
        _, err := d.EchoPipeToControl(
            fmt.Sprintf("set_default_active_thread_percentage %s", threadPercentage),
        )
        if err != nil { return ... }
    }

    // 8. 创建 .started 标记文件
    statusFile, err := os.Create(d.startedFile())
    if err != nil { return err }
    defer statusFile.Close()

    // 9. 启动日志跟踪器,将 control.log 转发到 stdout
    d.logTailer = newTailer(filepath.Join(logDir, "control.log"))
    if err := d.logTailer.Start(); err != nil {
        klog.ErrorS(err, "Could not start tail command on control.log; ignoring logs")
    }

    return nil
}

5.4 Stop() --- 停止 MPS Daemon

  1. EchoPipeToControl("quit") 发送退出命令
  2. 停止 log tailer
  3. setComputeMode(DEFAULT) 恢复 GPU 计算模式
  4. 删除 .started 文件
  5. os.RemoveAll(LogDir) 清理日志目录

5.5 EchoPipeToControl() --- MPS 控制命令通道

MPS Server nvidia-cuda-mps-control io.Pipe Daemon MPS Server nvidia-cuda-mps-control io.Pipe Daemon #mermaid-svg-eBZE6f5LhZOT6V5F{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-eBZE6f5LhZOT6V5F .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eBZE6f5LhZOT6V5F .error-icon{fill:#552222;}#mermaid-svg-eBZE6f5LhZOT6V5F .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eBZE6f5LhZOT6V5F .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eBZE6f5LhZOT6V5F .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eBZE6f5LhZOT6V5F .marker.cross{stroke:#333333;}#mermaid-svg-eBZE6f5LhZOT6V5F svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eBZE6f5LhZOT6V5F p{margin:0;}#mermaid-svg-eBZE6f5LhZOT6V5F .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eBZE6f5LhZOT6V5F text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-eBZE6f5LhZOT6V5F .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-eBZE6f5LhZOT6V5F .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-eBZE6f5LhZOT6V5F #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-eBZE6f5LhZOT6V5F .sequenceNumber{fill:white;}#mermaid-svg-eBZE6f5LhZOT6V5F #sequencenumber{fill:#333;}#mermaid-svg-eBZE6f5LhZOT6V5F #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-eBZE6f5LhZOT6V5F .messageText{fill:#333;stroke:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eBZE6f5LhZOT6V5F .labelText,#mermaid-svg-eBZE6f5LhZOT6V5F .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .loopText,#mermaid-svg-eBZE6f5LhZOT6V5F .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-eBZE6f5LhZOT6V5F .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-eBZE6f5LhZOT6V5F .noteText,#mermaid-svg-eBZE6f5LhZOT6V5F .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-eBZE6f5LhZOT6V5F .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eBZE6f5LhZOT6V5F .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eBZE6f5LhZOT6V5F .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-eBZE6f5LhZOT6V5F .actorPopupMenu{position:absolute;}#mermaid-svg-eBZE6f5LhZOT6V5F .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-eBZE6f5LhZOT6V5F .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-eBZE6f5LhZOT6V5F .actor-man circle,#mermaid-svg-eBZE6f5LhZOT6V5F line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-eBZE6f5LhZOT6V5F :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建 reader/writer pipe Start (stdin=reader, env=CUDA_MPS_*) Write("set_default_device_pinned_mem_limit 0 8192M") Close writer 通过管道发送命令 响应输出 Wait() 完成,out 包含响应 Close reader

go 复制代码
func (d *Daemon) EchoPipeToControl(command string) (string, error) {
    var out bytes.Buffer
    reader, writer := io.Pipe()       // 创建内存管道
    defer writer.Close()
    defer reader.Close()

    mpsDaemon := exec.Command(mpsControlBin)  // 不带 -d,交互模式
    mpsDaemon.Env = append(mpsDaemon.Env, d.EnvVars().toSlice()...)
    mpsDaemon.Stdin = reader                  // 从 pipe 读取命令
    mpsDaemon.Stdout = &out                   // 输出捕获到 buffer

    if err := mpsDaemon.Start(); err != nil { return "", ... }

    // 通过管道写入命令
    if _, err := writer.Write([]byte(command)); err != nil { return "", ... }
    writer.Close()  // 关闭写端,使 mps-control 的 stdin EOF

    if err := mpsDaemon.Wait(); err != nil { return "", ... }
    return out.String(), nil  // 返回命令输出
}

5.6 setComputeMode() --- GPU 计算模式切换

对所有关联 GPU 的 UUID,执行 nvidia-smi -i {uuid} -c {mode} 切换计算模式。MPS 要求 EXCLUSIVE_PROCESS 模式,停止时恢复为 DEFAULT

5.7 资源限制计算

perDevicePinnedDeviceMemoryLimits()
go 复制代码
func (m *Daemon) perDevicePinnedDeviceMemoryLimits() map[string]string {
    totalMemoryInBytesPerDevice := make(map[string]uint64)
    replicasPerDevice := make(map[string]uint64)

    // 遍历所有设备实例(一个 GPU 可能有多个副本)
    for _, device := range m.Devices() {
        index := device.Index
        totalMemoryInBytesPerDevice[index] = device.TotalMemory
        replicasPerDevice[index] += 1  // 同一 GPU 上的副本数累加
    }

    limits := make(map[string]string)
    for index, totalMemory := range totalMemoryInBytesPerDevice {
        if totalMemory == 0 { continue }  // 跳过无内存信息的设备
        replicas := replicasPerDevice[index]
        // 每副本可用内存 = 总内存 / 副本数,转换为 MB
        limits[index] = fmt.Sprintf("%vM", totalMemory/replicas/1024/1024)
    }
    return limits
}

示例:GPU 0 总显存 24GB(=25769803776 bytes),配置 4 副本:

  • limit = 25769803776 / 4 / 1024 / 1024 = 6144M
activeThreadPercentage()
go 复制代码
func (m *Daemon) activeThreadPercentage() string {
    if len(m.Devices()) == 0 { return "" }
    // 设备实例总数 / GPU 数 = 每 GPU 副本数
    replicasPerDevice := len(m.Devices()) / len(m.Devices().GetUUIDs())
    return fmt.Sprintf("%d", 100/replicasPerDevice)
}

示例:2 块 GPU,每块 4 副本 → 总设备实例 8 / 2 = 4 → 100/4 = 25%

5.8 EnvVars() --- 环境变量

返回 CUDA_MPS_PIPE_DIRECTORYCUDA_MPS_LOG_DIRECTORY 两个环境变量,需传递给使用 MPS 共享 GPU 的容器。MPS 客户端通过管道目录找到服务端通信。

5.9 setSELinuxContext()

检查 /sys/fs/selinux 是否存在判断 SELinux 是否启用。若启用,调用 selinux.Chcon(path, context, true) 递归设置 SELinux 上下文为 system_u:object_r:container_file_t:s0,确保非特权容器可访问管道目录。


6. MPS Device 设备校验

6.1 文件:mps/device.go

此文件定义了 mpsDevice 类型别名和设备校验逻辑。

go 复制代码
// mpsDevice 是 rm.Device 的类型别名
type mpsDevice rm.Device

6.2 assertReplicas() --- 副本数校验

go 复制代码
func (d *mpsDevice) assertReplicas() error {
    maxClients := d.maxClients()
    if d.Replicas > maxClients {
        return fmt.Errorf("%w maximum allowed replicas exceeded: %d > %d",
            errInvalidDevice, d.Replicas, maxClients)
    }
    return nil
}

6.3 maxClients() --- MPS 最大客户端数

go 复制代码
func (d *mpsDevice) maxClients() int {
    if d.isAtLeastVolta() {
        return 48   // Volta 及以上架构支持 48 个 MPS 客户端
    }
    return 16       // 较老架构仅支持 16 个
}

6.4 isAtLeastVolta() --- 架构检测

go 复制代码
func (d *mpsDevice) isAtLeastVolta() bool {
    // 将计算能力格式化为语义版本号
    vCc := "v" + strings.TrimPrefix(d.ComputeCapability, "v")
    // semver.Compare 比较版本号:>= 7.5 即为 Volta 及以上
    return semver.Compare(semver.Canonical(vCc), semver.Canonical("v7.5")) >= 0
}

架构与 MPS 客户端限制

  • Volta (7.5+) 及以上(Turing, Ampere, Hopper)支持 48 个并发 MPS 客户端
  • Pascal 及更早架构仅支持 16 个
  • 此校验确保配置的副本数不超过硬件限制
    #mermaid-svg-0LmoJGCtHnyO05go{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-0LmoJGCtHnyO05go .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-0LmoJGCtHnyO05go .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-0LmoJGCtHnyO05go .error-icon{fill:#552222;}#mermaid-svg-0LmoJGCtHnyO05go .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-0LmoJGCtHnyO05go .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-0LmoJGCtHnyO05go .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-0LmoJGCtHnyO05go .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-0LmoJGCtHnyO05go .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-0LmoJGCtHnyO05go .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-0LmoJGCtHnyO05go .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-0LmoJGCtHnyO05go .marker{fill:#333333;stroke:#333333;}#mermaid-svg-0LmoJGCtHnyO05go .marker.cross{stroke:#333333;}#mermaid-svg-0LmoJGCtHnyO05go svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-0LmoJGCtHnyO05go p{margin:0;}#mermaid-svg-0LmoJGCtHnyO05go .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-0LmoJGCtHnyO05go .cluster-label text{fill:#333;}#mermaid-svg-0LmoJGCtHnyO05go .cluster-label span{color:#333;}#mermaid-svg-0LmoJGCtHnyO05go .cluster-label span p{background-color:transparent;}#mermaid-svg-0LmoJGCtHnyO05go .label text,#mermaid-svg-0LmoJGCtHnyO05go span{fill:#333;color:#333;}#mermaid-svg-0LmoJGCtHnyO05go .node rect,#mermaid-svg-0LmoJGCtHnyO05go .node circle,#mermaid-svg-0LmoJGCtHnyO05go .node ellipse,#mermaid-svg-0LmoJGCtHnyO05go .node polygon,#mermaid-svg-0LmoJGCtHnyO05go .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-0LmoJGCtHnyO05go .rough-node .label text,#mermaid-svg-0LmoJGCtHnyO05go .node .label text,#mermaid-svg-0LmoJGCtHnyO05go .image-shape .label,#mermaid-svg-0LmoJGCtHnyO05go .icon-shape .label{text-anchor:middle;}#mermaid-svg-0LmoJGCtHnyO05go .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-0LmoJGCtHnyO05go .rough-node .label,#mermaid-svg-0LmoJGCtHnyO05go .node .label,#mermaid-svg-0LmoJGCtHnyO05go .image-shape .label,#mermaid-svg-0LmoJGCtHnyO05go .icon-shape .label{text-align:center;}#mermaid-svg-0LmoJGCtHnyO05go .node.clickable{cursor:pointer;}#mermaid-svg-0LmoJGCtHnyO05go .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-0LmoJGCtHnyO05go .arrowheadPath{fill:#333333;}#mermaid-svg-0LmoJGCtHnyO05go .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-0LmoJGCtHnyO05go .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-0LmoJGCtHnyO05go .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0LmoJGCtHnyO05go .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-0LmoJGCtHnyO05go .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0LmoJGCtHnyO05go .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-0LmoJGCtHnyO05go .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-0LmoJGCtHnyO05go .cluster text{fill:#333;}#mermaid-svg-0LmoJGCtHnyO05go .cluster span{color:#333;}#mermaid-svg-0LmoJGCtHnyO05go 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-0LmoJGCtHnyO05go .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-0LmoJGCtHnyO05go rect.text{fill:none;stroke-width:0;}#mermaid-svg-0LmoJGCtHnyO05go .icon-shape,#mermaid-svg-0LmoJGCtHnyO05go .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-0LmoJGCtHnyO05go .icon-shape p,#mermaid-svg-0LmoJGCtHnyO05go .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-0LmoJGCtHnyO05go .icon-shape .label rect,#mermaid-svg-0LmoJGCtHnyO05go .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-0LmoJGCtHnyO05go .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-0LmoJGCtHnyO05go .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-0LmoJGCtHnyO05go :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是



    mpsDevice
    ComputeCapability >= 7.5?
    maxClients = 48

Volta/Turing/Ampere/Hopper
maxClients = 16

Pascal/Maxwell/Kepler
Replicas > maxClients?
返回错误

errInvalidDevice
校验通过


7. MPS Log Tailer 日志跟踪

7.1 文件:mps/log-tailer.go

日志跟踪器使用 tail -f 命令持续读取 MPS 控制进程的日志文件,将输出转发到当前进程的 stdout/stderr。

7.2 数据结构

go 复制代码
type tailer struct {
    filename string              // 跟踪的文件路径
    cmd      *exec.Cmd           // tail 命令实例
    cancel   context.CancelFunc  // 用于取消 tail 命令
}

7.3 Start() --- 启动日志跟踪

创建可取消 context,执行 tail -n +1 -f {filename}(从第1行开始、持续跟踪),将 stdout/stderr 转发到当前进程的 os.Stdout/os.Stderr。

7.4 Stop() --- 停止日志跟踪

调用 cancel() 取消 context(tail 进程收到终止信号),然后 cmd.Wait() 等待 tail 进程退出。

7.5 Log Tailer 流程

stdout control.log tail -f process Tailer Daemon stdout control.log tail -f process Tailer Daemon #mermaid-svg-ka70AYXcJhdc3OPA{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-ka70AYXcJhdc3OPA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ka70AYXcJhdc3OPA .error-icon{fill:#552222;}#mermaid-svg-ka70AYXcJhdc3OPA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ka70AYXcJhdc3OPA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ka70AYXcJhdc3OPA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ka70AYXcJhdc3OPA .marker.cross{stroke:#333333;}#mermaid-svg-ka70AYXcJhdc3OPA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ka70AYXcJhdc3OPA p{margin:0;}#mermaid-svg-ka70AYXcJhdc3OPA .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ka70AYXcJhdc3OPA text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ka70AYXcJhdc3OPA .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ka70AYXcJhdc3OPA .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ka70AYXcJhdc3OPA .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ka70AYXcJhdc3OPA .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ka70AYXcJhdc3OPA #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ka70AYXcJhdc3OPA .sequenceNumber{fill:white;}#mermaid-svg-ka70AYXcJhdc3OPA #sequencenumber{fill:#333;}#mermaid-svg-ka70AYXcJhdc3OPA #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ka70AYXcJhdc3OPA .messageText{fill:#333;stroke:none;}#mermaid-svg-ka70AYXcJhdc3OPA .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ka70AYXcJhdc3OPA .labelText,#mermaid-svg-ka70AYXcJhdc3OPA .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ka70AYXcJhdc3OPA .loopText,#mermaid-svg-ka70AYXcJhdc3OPA .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ka70AYXcJhdc3OPA .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ka70AYXcJhdc3OPA .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ka70AYXcJhdc3OPA .noteText,#mermaid-svg-ka70AYXcJhdc3OPA .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ka70AYXcJhdc3OPA .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ka70AYXcJhdc3OPA .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ka70AYXcJhdc3OPA .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ka70AYXcJhdc3OPA .actorPopupMenu{position:absolute;}#mermaid-svg-ka70AYXcJhdc3OPA .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ka70AYXcJhdc3OPA .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ka70AYXcJhdc3OPA .actor-man circle,#mermaid-svg-ka70AYXcJhdc3OPA line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ka70AYXcJhdc3OPA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} newTailer("/mps/Resource/log/control.log") Start() exec.CommandContext(tail -n +1 -f control.log) cmd.Stdout = os.Stdout 持续读取新增内容 新日志行 转发日志 Stop() cancel() → context 取消 进程退出 cmd.Wait() 返回


8. MPS Manager 守护进程管理器

8.1 文件:mps/manager.go

Manager 负责根据配置创建一组 Daemon 实例,每个 Daemon 对应一个共享资源配置。

8.2 接口定义

go 复制代码
type Manager interface {
    Daemons() ([]*Daemon, error)
}

8.3 实现类

go 复制代码
// 正常 manager:根据配置创建 Daemon
type manager struct {
    infolib   info.Interface
    nvmllib   nvml.Interface
    devicelib device.Interface
    config    *spec.Config
}

// null manager:不创建任何 Daemon(非 MPS 共享模式时使用)
type nullManager struct{}

8.4 New() --- 工厂函数

go 复制代码
func New(infolib info.Interface, nvmllib nvml.Interface, devicelib device.Interface, opts ...Option) (Manager, error) {
    m := &manager{
        infolib:   infolib,
        nvmllib:   nvmllib,
        devicelib: devicelib,
    }
    for _, opt := range opts {
        opt(m)  // 应用函数式选项
    }

    // 检查共享策略:如果不是 MPS,返回 nullManager
    if strategy := m.config.Sharing.SharingStrategy(); strategy != spec.SharingStrategyMPS {
        klog.InfoS("Sharing strategy is not MPS; skipping MPS manager creation", "strategy", strategy)
        return &nullManager{}, nil
    }

    return m, nil
}

8.5 Daemons() --- 创建 Daemon 集合

#mermaid-svg-7hEP7tECObk5OF2H{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-7hEP7tECObk5OF2H .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7hEP7tECObk5OF2H .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7hEP7tECObk5OF2H .error-icon{fill:#552222;}#mermaid-svg-7hEP7tECObk5OF2H .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7hEP7tECObk5OF2H .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7hEP7tECObk5OF2H .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7hEP7tECObk5OF2H .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7hEP7tECObk5OF2H .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7hEP7tECObk5OF2H .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7hEP7tECObk5OF2H .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7hEP7tECObk5OF2H .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7hEP7tECObk5OF2H .marker.cross{stroke:#333333;}#mermaid-svg-7hEP7tECObk5OF2H svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7hEP7tECObk5OF2H p{margin:0;}#mermaid-svg-7hEP7tECObk5OF2H .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7hEP7tECObk5OF2H .cluster-label text{fill:#333;}#mermaid-svg-7hEP7tECObk5OF2H .cluster-label span{color:#333;}#mermaid-svg-7hEP7tECObk5OF2H .cluster-label span p{background-color:transparent;}#mermaid-svg-7hEP7tECObk5OF2H .label text,#mermaid-svg-7hEP7tECObk5OF2H span{fill:#333;color:#333;}#mermaid-svg-7hEP7tECObk5OF2H .node rect,#mermaid-svg-7hEP7tECObk5OF2H .node circle,#mermaid-svg-7hEP7tECObk5OF2H .node ellipse,#mermaid-svg-7hEP7tECObk5OF2H .node polygon,#mermaid-svg-7hEP7tECObk5OF2H .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7hEP7tECObk5OF2H .rough-node .label text,#mermaid-svg-7hEP7tECObk5OF2H .node .label text,#mermaid-svg-7hEP7tECObk5OF2H .image-shape .label,#mermaid-svg-7hEP7tECObk5OF2H .icon-shape .label{text-anchor:middle;}#mermaid-svg-7hEP7tECObk5OF2H .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7hEP7tECObk5OF2H .rough-node .label,#mermaid-svg-7hEP7tECObk5OF2H .node .label,#mermaid-svg-7hEP7tECObk5OF2H .image-shape .label,#mermaid-svg-7hEP7tECObk5OF2H .icon-shape .label{text-align:center;}#mermaid-svg-7hEP7tECObk5OF2H .node.clickable{cursor:pointer;}#mermaid-svg-7hEP7tECObk5OF2H .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7hEP7tECObk5OF2H .arrowheadPath{fill:#333333;}#mermaid-svg-7hEP7tECObk5OF2H .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7hEP7tECObk5OF2H .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7hEP7tECObk5OF2H .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7hEP7tECObk5OF2H .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7hEP7tECObk5OF2H .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7hEP7tECObk5OF2H .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7hEP7tECObk5OF2H .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7hEP7tECObk5OF2H .cluster text{fill:#333;}#mermaid-svg-7hEP7tECObk5OF2H .cluster span{color:#333;}#mermaid-svg-7hEP7tECObk5OF2H 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-7hEP7tECObk5OF2H .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7hEP7tECObk5OF2H rect.text{fill:none;stroke-width:0;}#mermaid-svg-7hEP7tECObk5OF2H .icon-shape,#mermaid-svg-7hEP7tECObk5OF2H .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7hEP7tECObk5OF2H .icon-shape p,#mermaid-svg-7hEP7tECObk5OF2H .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7hEP7tECObk5OF2H .icon-shape .label rect,#mermaid-svg-7hEP7tECObk5OF2H .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7hEP7tECObk5OF2H .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7hEP7tECObk5OF2H .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7hEP7tECObk5OF2H :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否







manager.Daemons
创建 NVML ResourceManagers
遍历每个 ResourceManager
设备数 > 0 且共享?
跳过
包含 MIG 设备?
警告跳过
assertReplicas 校验
校验通过?
返回错误
创建 NewDaemon
更多 ResourceManager?
返回 daemons 列表

核心逻辑:

  1. rm.NewNVMLResourceManagers() 创建资源管理器集合
  2. 跳过无设备的资源
  3. 跳过非共享资源(设备 ID 无注解)
  4. MIG 设备不支持 MPS,跳过并警告
  5. (*mpsDevice)(rmDevice).assertReplicas() 校验副本数
  6. NewDaemon(resourceManager, ContainerRoot) 创建 Daemon

8.6 Manager 类图

#mermaid-svg-dwhOJN60F2msUrzP{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-dwhOJN60F2msUrzP .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dwhOJN60F2msUrzP .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dwhOJN60F2msUrzP .error-icon{fill:#552222;}#mermaid-svg-dwhOJN60F2msUrzP .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dwhOJN60F2msUrzP .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dwhOJN60F2msUrzP .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dwhOJN60F2msUrzP .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dwhOJN60F2msUrzP .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dwhOJN60F2msUrzP .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dwhOJN60F2msUrzP .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dwhOJN60F2msUrzP .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dwhOJN60F2msUrzP .marker.cross{stroke:#333333;}#mermaid-svg-dwhOJN60F2msUrzP svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dwhOJN60F2msUrzP p{margin:0;}#mermaid-svg-dwhOJN60F2msUrzP g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-dwhOJN60F2msUrzP g.classGroup text .title{font-weight:bolder;}#mermaid-svg-dwhOJN60F2msUrzP .cluster-label text{fill:#333;}#mermaid-svg-dwhOJN60F2msUrzP .cluster-label span{color:#333;}#mermaid-svg-dwhOJN60F2msUrzP .cluster-label span p{background-color:transparent;}#mermaid-svg-dwhOJN60F2msUrzP .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dwhOJN60F2msUrzP .cluster text{fill:#333;}#mermaid-svg-dwhOJN60F2msUrzP .cluster span{color:#333;}#mermaid-svg-dwhOJN60F2msUrzP .nodeLabel,#mermaid-svg-dwhOJN60F2msUrzP .edgeLabel{color:#131300;}#mermaid-svg-dwhOJN60F2msUrzP .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-dwhOJN60F2msUrzP .label text{fill:#131300;}#mermaid-svg-dwhOJN60F2msUrzP .labelBkg{background:#ECECFF;}#mermaid-svg-dwhOJN60F2msUrzP .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-dwhOJN60F2msUrzP .classTitle{font-weight:bolder;}#mermaid-svg-dwhOJN60F2msUrzP .node rect,#mermaid-svg-dwhOJN60F2msUrzP .node circle,#mermaid-svg-dwhOJN60F2msUrzP .node ellipse,#mermaid-svg-dwhOJN60F2msUrzP .node polygon,#mermaid-svg-dwhOJN60F2msUrzP .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dwhOJN60F2msUrzP .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP g.clickable{cursor:pointer;}#mermaid-svg-dwhOJN60F2msUrzP g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-dwhOJN60F2msUrzP g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-dwhOJN60F2msUrzP .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-dwhOJN60F2msUrzP .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-dwhOJN60F2msUrzP .dashed-line{stroke-dasharray:3;}#mermaid-svg-dwhOJN60F2msUrzP .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-dwhOJN60F2msUrzP #compositionStart,#mermaid-svg-dwhOJN60F2msUrzP .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #compositionEnd,#mermaid-svg-dwhOJN60F2msUrzP .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #dependencyStart,#mermaid-svg-dwhOJN60F2msUrzP .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #dependencyStart,#mermaid-svg-dwhOJN60F2msUrzP .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #extensionStart,#mermaid-svg-dwhOJN60F2msUrzP .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #extensionEnd,#mermaid-svg-dwhOJN60F2msUrzP .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #aggregationStart,#mermaid-svg-dwhOJN60F2msUrzP .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #aggregationEnd,#mermaid-svg-dwhOJN60F2msUrzP .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #lollipopStart,#mermaid-svg-dwhOJN60F2msUrzP .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP #lollipopEnd,#mermaid-svg-dwhOJN60F2msUrzP .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-dwhOJN60F2msUrzP .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-dwhOJN60F2msUrzP .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dwhOJN60F2msUrzP .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dwhOJN60F2msUrzP .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dwhOJN60F2msUrzP :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} creates
<<interface>>
Manager
+Daemons()(\[\]*Daemon, error)
manager
-infolib info.Interface
-nvmllib nvml.Interface
-devicelib device.Interface
-config *spec.Config
+Daemons()(\[\]*Daemon, error)
nullManager
+Daemons()(\[\]*Daemon, error)
Daemon
-rm rm.ResourceManager
-root Root
-logTailer *tailer
+Start() : error
+Stop() : error
+Devices() : rm.Devices
+EnvVars() : envvars
+AssertHealthy() : error
+EchoPipeToControl(cmd string)(string, error)


9. MPS Options 配置选项

9.1 文件:mps/options.go

采用函数式选项模式Optionfunc(*manager) 类型,WithConfig() 返回设置 config 的闭包。使用方式:mps.NewDaemons(..., mps.WithConfig(config))。设计意图:允许未来添加更多配置选项而不改变函数签名。


10. MPS Root 路径管理

10.1 文件:mps/root.go

Root 类型管理 MPS 相关的目录结构,确保每个资源共享资源有独立的管道和日志空间。

10.2 类型与常量

go 复制代码
const ContainerRoot = Root("/mps")  // 容器内默认根路径

type Root string  // Root 是一个字符串类型

10.3 路径计算方法

go 复制代码
// LogDir: /mps/{resourceName}/log
func (r Root) LogDir(resourceName spec.ResourceName) string {
    return r.Path(string(resourceName), "log")
}

// PipeDir: /mps/{resourceName}/pipe
func (r Root) PipeDir(resourceName spec.ResourceName) string {
    return r.Path(string(resourceName), "pipe")
}

// ShmDir: /mps/shm (所有资源共享)
func (r Root) ShmDir(resourceName spec.ResourceName) string {
    return r.Path("shm")
}

// startedFile: /mps/{resourceName}/.started
func (r Root) startedFile(resourceName spec.ResourceName) string {
    return r.Path(string(resourceName), ".started")
}

// Path: 拼接根路径与子路径
func (r Root) Path(parts ...string) string {
    pathparts := append([]string{string(r)}, parts...)
    return filepath.Join(pathparts...)
}

10.4 目录结构图

复制代码
/mps/                          ← Root (/mps)
├── .ready                     ← 全局就绪标记文件
├── shm/                       ← 共享内存目录(所有资源共享)
│   └── (tmpfs 挂载)
├── nvidia.com_gpu/            ← 资源 nvidia.com/gpu 的目录
│   ├── .started               ← 此资源 Daemon 已启动标记
│   ├── pipe/                  ← MPS 管道目录
│   │   └── nvidia-cuda-mps-control
│   └── log/                   ← MPS 日志目录
│       └── control.log
├── nvidia.com_gpu_shared/     ← 资源 nvidia.com/gpu.shared 的目录
│   ├── .started
│   ├── pipe/
│   └── log/
│       └── control.log
└── ...

#mermaid-svg-6TZlUIZOUnu3I8ol{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-6TZlUIZOUnu3I8ol .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6TZlUIZOUnu3I8ol .error-icon{fill:#552222;}#mermaid-svg-6TZlUIZOUnu3I8ol .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6TZlUIZOUnu3I8ol .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6TZlUIZOUnu3I8ol .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6TZlUIZOUnu3I8ol .marker.cross{stroke:#333333;}#mermaid-svg-6TZlUIZOUnu3I8ol svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6TZlUIZOUnu3I8ol p{margin:0;}#mermaid-svg-6TZlUIZOUnu3I8ol .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster-label text{fill:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster-label span{color:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster-label span p{background-color:transparent;}#mermaid-svg-6TZlUIZOUnu3I8ol .label text,#mermaid-svg-6TZlUIZOUnu3I8ol span{fill:#333;color:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol .node rect,#mermaid-svg-6TZlUIZOUnu3I8ol .node circle,#mermaid-svg-6TZlUIZOUnu3I8ol .node ellipse,#mermaid-svg-6TZlUIZOUnu3I8ol .node polygon,#mermaid-svg-6TZlUIZOUnu3I8ol .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6TZlUIZOUnu3I8ol .rough-node .label text,#mermaid-svg-6TZlUIZOUnu3I8ol .node .label text,#mermaid-svg-6TZlUIZOUnu3I8ol .image-shape .label,#mermaid-svg-6TZlUIZOUnu3I8ol .icon-shape .label{text-anchor:middle;}#mermaid-svg-6TZlUIZOUnu3I8ol .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6TZlUIZOUnu3I8ol .rough-node .label,#mermaid-svg-6TZlUIZOUnu3I8ol .node .label,#mermaid-svg-6TZlUIZOUnu3I8ol .image-shape .label,#mermaid-svg-6TZlUIZOUnu3I8ol .icon-shape .label{text-align:center;}#mermaid-svg-6TZlUIZOUnu3I8ol .node.clickable{cursor:pointer;}#mermaid-svg-6TZlUIZOUnu3I8ol .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6TZlUIZOUnu3I8ol .arrowheadPath{fill:#333333;}#mermaid-svg-6TZlUIZOUnu3I8ol .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6TZlUIZOUnu3I8ol .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6TZlUIZOUnu3I8ol .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6TZlUIZOUnu3I8ol .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6TZlUIZOUnu3I8ol .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6TZlUIZOUnu3I8ol .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster text{fill:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol .cluster span{color:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol 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-6TZlUIZOUnu3I8ol .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6TZlUIZOUnu3I8ol rect.text{fill:none;stroke-width:0;}#mermaid-svg-6TZlUIZOUnu3I8ol .icon-shape,#mermaid-svg-6TZlUIZOUnu3I8ol .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6TZlUIZOUnu3I8ol .icon-shape p,#mermaid-svg-6TZlUIZOUnu3I8ol .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6TZlUIZOUnu3I8ol .icon-shape .label rect,#mermaid-svg-6TZlUIZOUnu3I8ol .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6TZlUIZOUnu3I8ol .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6TZlUIZOUnu3I8ol .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6TZlUIZOUnu3I8ol :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} /mps (ContainerRoot)
.ready
shm/ (tmpfs)
nvidia.com_gpu/
.started
pipe/
log/control.log
nvidia.com_gpu_shared/
.started
pipe/
log/control.log


11. Mount SHM 共享内存挂载

11.1 文件:mount/mount-shm.go

此文件实现 mount-shm 子命令,为 MPS daemon 创建专用的 tmpfs 共享内存挂载。

11.2 NewCommand() --- 子命令注册

go 复制代码
func NewCommand() *cli.Command {
    return &cli.Command{
        Name:   "mount-shm",
        Usage:  "Set up the /dev/shm mount required by the MPS daemon",
        Action: mountShm,
    }
}

此命令在 main.go 中通过 c.Commands = []*cli.Command{mount.NewCommand()} 注册为子命令,调用方式:mps-control-daemon mount-shm

11.3 mountShm() --- 挂载逻辑

#mermaid-svg-Yf75IWcJAl0UMrXk{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-Yf75IWcJAl0UMrXk .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Yf75IWcJAl0UMrXk .error-icon{fill:#552222;}#mermaid-svg-Yf75IWcJAl0UMrXk .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Yf75IWcJAl0UMrXk .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Yf75IWcJAl0UMrXk .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Yf75IWcJAl0UMrXk .marker.cross{stroke:#333333;}#mermaid-svg-Yf75IWcJAl0UMrXk svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Yf75IWcJAl0UMrXk p{margin:0;}#mermaid-svg-Yf75IWcJAl0UMrXk .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster-label text{fill:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster-label span{color:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster-label span p{background-color:transparent;}#mermaid-svg-Yf75IWcJAl0UMrXk .label text,#mermaid-svg-Yf75IWcJAl0UMrXk span{fill:#333;color:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk .node rect,#mermaid-svg-Yf75IWcJAl0UMrXk .node circle,#mermaid-svg-Yf75IWcJAl0UMrXk .node ellipse,#mermaid-svg-Yf75IWcJAl0UMrXk .node polygon,#mermaid-svg-Yf75IWcJAl0UMrXk .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Yf75IWcJAl0UMrXk .rough-node .label text,#mermaid-svg-Yf75IWcJAl0UMrXk .node .label text,#mermaid-svg-Yf75IWcJAl0UMrXk .image-shape .label,#mermaid-svg-Yf75IWcJAl0UMrXk .icon-shape .label{text-anchor:middle;}#mermaid-svg-Yf75IWcJAl0UMrXk .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Yf75IWcJAl0UMrXk .rough-node .label,#mermaid-svg-Yf75IWcJAl0UMrXk .node .label,#mermaid-svg-Yf75IWcJAl0UMrXk .image-shape .label,#mermaid-svg-Yf75IWcJAl0UMrXk .icon-shape .label{text-align:center;}#mermaid-svg-Yf75IWcJAl0UMrXk .node.clickable{cursor:pointer;}#mermaid-svg-Yf75IWcJAl0UMrXk .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Yf75IWcJAl0UMrXk .arrowheadPath{fill:#333333;}#mermaid-svg-Yf75IWcJAl0UMrXk .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Yf75IWcJAl0UMrXk .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Yf75IWcJAl0UMrXk .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Yf75IWcJAl0UMrXk .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Yf75IWcJAl0UMrXk .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Yf75IWcJAl0UMrXk .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster text{fill:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk .cluster span{color:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk 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-Yf75IWcJAl0UMrXk .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Yf75IWcJAl0UMrXk rect.text{fill:none;stroke-width:0;}#mermaid-svg-Yf75IWcJAl0UMrXk .icon-shape,#mermaid-svg-Yf75IWcJAl0UMrXk .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Yf75IWcJAl0UMrXk .icon-shape p,#mermaid-svg-Yf75IWcJAl0UMrXk .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Yf75IWcJAl0UMrXk .icon-shape .label rect,#mermaid-svg-Yf75IWcJAl0UMrXk .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Yf75IWcJAl0UMrXk .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Yf75IWcJAl0UMrXk .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Yf75IWcJAl0UMrXk :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} mountShm
查找 mount 可执行文件
清理旧挂载点 /mps/shm
创建目录 /mps/shm
计算 tmpfs 大小
mount -t tmpfs -o rw,nosuid,nodev,noexec,relatime,size=XXX shm /mps/shm
完成

使用 k8s.io/mount-utils 库的 mount.New() 创建挂载器,先 CleanupMountPoint 卸载旧挂载,再 MkdirAll 创建目录,最后以 rw,nosuid,nodev,noexec,relatime,size=XXX 选项挂载 tmpfs。

11.4 getDefaultShmSize() --- 智能大小计算

go 复制代码
func getDefaultShmSize() string {
    const fallbackSize = "65536k"  // 默认 64MB

    // 读取 /proc/meminfo
    meminfo, err := os.Open("/proc/meminfo")
    if err != nil { return fallbackSize }
    defer meminfo.Close()

    scanner := bufio.NewScanner(meminfo)
    for scanner.Scan() {
        line := scanner.Text()
        if !strings.HasPrefix(line, "MemTotal:") { continue }

        // 解析 "MemTotal:       32812345 kB"
        parts := strings.SplitN(
            strings.TrimSpace(strings.TrimPrefix(line, "MemTotal:")),
            " ", 2,
        )
        memTotal, err := strconv.Atoi(parts[0])
        if err != nil { return fallbackSize }

        // 提取单位(kB, MB 等)
        var unit string
        if len(parts) == 2 {
            unit = string(parts[1][0])  // 取第一个字符作为单位
        }

        // shm size = MemTotal / 2
        return fmt.Sprintf("%d%s", memTotal/2, unit)
    }
    return fallbackSize
}

示例 :系统总内存 128GB → /proc/meminfoMemTotal: 134217728 kB → shm size = 67108864 kB ≈ 64GB

11.5 SHM 挂载流程图

#mermaid-svg-KdU9qhRnj5tj8maC{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-KdU9qhRnj5tj8maC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-KdU9qhRnj5tj8maC .error-icon{fill:#552222;}#mermaid-svg-KdU9qhRnj5tj8maC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-KdU9qhRnj5tj8maC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-KdU9qhRnj5tj8maC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-KdU9qhRnj5tj8maC .marker.cross{stroke:#333333;}#mermaid-svg-KdU9qhRnj5tj8maC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-KdU9qhRnj5tj8maC p{margin:0;}#mermaid-svg-KdU9qhRnj5tj8maC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-KdU9qhRnj5tj8maC .cluster-label text{fill:#333;}#mermaid-svg-KdU9qhRnj5tj8maC .cluster-label span{color:#333;}#mermaid-svg-KdU9qhRnj5tj8maC .cluster-label span p{background-color:transparent;}#mermaid-svg-KdU9qhRnj5tj8maC .label text,#mermaid-svg-KdU9qhRnj5tj8maC span{fill:#333;color:#333;}#mermaid-svg-KdU9qhRnj5tj8maC .node rect,#mermaid-svg-KdU9qhRnj5tj8maC .node circle,#mermaid-svg-KdU9qhRnj5tj8maC .node ellipse,#mermaid-svg-KdU9qhRnj5tj8maC .node polygon,#mermaid-svg-KdU9qhRnj5tj8maC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-KdU9qhRnj5tj8maC .rough-node .label text,#mermaid-svg-KdU9qhRnj5tj8maC .node .label text,#mermaid-svg-KdU9qhRnj5tj8maC .image-shape .label,#mermaid-svg-KdU9qhRnj5tj8maC .icon-shape .label{text-anchor:middle;}#mermaid-svg-KdU9qhRnj5tj8maC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-KdU9qhRnj5tj8maC .rough-node .label,#mermaid-svg-KdU9qhRnj5tj8maC .node .label,#mermaid-svg-KdU9qhRnj5tj8maC .image-shape .label,#mermaid-svg-KdU9qhRnj5tj8maC .icon-shape .label{text-align:center;}#mermaid-svg-KdU9qhRnj5tj8maC .node.clickable{cursor:pointer;}#mermaid-svg-KdU9qhRnj5tj8maC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-KdU9qhRnj5tj8maC .arrowheadPath{fill:#333333;}#mermaid-svg-KdU9qhRnj5tj8maC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-KdU9qhRnj5tj8maC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-KdU9qhRnj5tj8maC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KdU9qhRnj5tj8maC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-KdU9qhRnj5tj8maC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KdU9qhRnj5tj8maC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-KdU9qhRnj5tj8maC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-KdU9qhRnj5tj8maC .cluster text{fill:#333;}#mermaid-svg-KdU9qhRnj5tj8maC .cluster span{color:#333;}#mermaid-svg-KdU9qhRnj5tj8maC 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-KdU9qhRnj5tj8maC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-KdU9qhRnj5tj8maC rect.text{fill:none;stroke-width:0;}#mermaid-svg-KdU9qhRnj5tj8maC .icon-shape,#mermaid-svg-KdU9qhRnj5tj8maC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-KdU9qhRnj5tj8maC .icon-shape p,#mermaid-svg-KdU9qhRnj5tj8maC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-KdU9qhRnj5tj8maC .icon-shape .label rect,#mermaid-svg-KdU9qhRnj5tj8maC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-KdU9qhRnj5tj8maC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-KdU9qhRnj5tj8maC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-KdU9qhRnj5tj8maC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 找到
未找到
mps-control-daemon mount-shm
LookPath mount
CleanupMountPoint(/mps/shm)
MkdirAll(/mps/shm, 0755)
Open /proc/meminfo
读取 MemTotal 行
计算 size = MemTotal/2
使用 fallback: 65536k
mount -t tmpfs -o rw,nosuid,nodev,noexec,relatime,size=XXX shm /mps/shm
完成


12. 全局架构与交互关系

12.1 三个辅助命令协作关系

#mermaid-svg-hbMRRhEAx6z6WeQr{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-hbMRRhEAx6z6WeQr .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hbMRRhEAx6z6WeQr .error-icon{fill:#552222;}#mermaid-svg-hbMRRhEAx6z6WeQr .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hbMRRhEAx6z6WeQr .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hbMRRhEAx6z6WeQr .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hbMRRhEAx6z6WeQr .marker.cross{stroke:#333333;}#mermaid-svg-hbMRRhEAx6z6WeQr svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hbMRRhEAx6z6WeQr p{margin:0;}#mermaid-svg-hbMRRhEAx6z6WeQr .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster-label text{fill:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster-label span{color:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster-label span p{background-color:transparent;}#mermaid-svg-hbMRRhEAx6z6WeQr .label text,#mermaid-svg-hbMRRhEAx6z6WeQr span{fill:#333;color:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr .node rect,#mermaid-svg-hbMRRhEAx6z6WeQr .node circle,#mermaid-svg-hbMRRhEAx6z6WeQr .node ellipse,#mermaid-svg-hbMRRhEAx6z6WeQr .node polygon,#mermaid-svg-hbMRRhEAx6z6WeQr .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hbMRRhEAx6z6WeQr .rough-node .label text,#mermaid-svg-hbMRRhEAx6z6WeQr .node .label text,#mermaid-svg-hbMRRhEAx6z6WeQr .image-shape .label,#mermaid-svg-hbMRRhEAx6z6WeQr .icon-shape .label{text-anchor:middle;}#mermaid-svg-hbMRRhEAx6z6WeQr .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hbMRRhEAx6z6WeQr .rough-node .label,#mermaid-svg-hbMRRhEAx6z6WeQr .node .label,#mermaid-svg-hbMRRhEAx6z6WeQr .image-shape .label,#mermaid-svg-hbMRRhEAx6z6WeQr .icon-shape .label{text-align:center;}#mermaid-svg-hbMRRhEAx6z6WeQr .node.clickable{cursor:pointer;}#mermaid-svg-hbMRRhEAx6z6WeQr .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hbMRRhEAx6z6WeQr .arrowheadPath{fill:#333333;}#mermaid-svg-hbMRRhEAx6z6WeQr .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hbMRRhEAx6z6WeQr .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hbMRRhEAx6z6WeQr .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hbMRRhEAx6z6WeQr .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hbMRRhEAx6z6WeQr .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hbMRRhEAx6z6WeQr .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster text{fill:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr .cluster span{color:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr 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-hbMRRhEAx6z6WeQr .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hbMRRhEAx6z6WeQr rect.text{fill:none;stroke-width:0;}#mermaid-svg-hbMRRhEAx6z6WeQr .icon-shape,#mermaid-svg-hbMRRhEAx6z6WeQr .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hbMRRhEAx6z6WeQr .icon-shape p,#mermaid-svg-hbMRRhEAx6z6WeQr .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hbMRRhEAx6z6WeQr .icon-shape .label rect,#mermaid-svg-hbMRRhEAx6z6WeQr .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hbMRRhEAx6z6WeQr .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hbMRRhEAx6z6WeQr .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hbMRRhEAx6z6WeQr :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Kubernetes 集群
工作节点
控制面
GPU 硬件
文件系统
Sidecar / DaemonSet
Init Container
SIGHUP
GPU 0

EXCLUSIVE_PROCESS
gpu-feature-discovery

生成 GPU 标签
/etc/kubernetes/.../features.d/gfd

GFD 输出文件
Kubernetes API Server
config-manager

监听标签 → 切换配置
/etc/nvidia-config-manager/

ConfigMap 挂载源目录
/etc/nvidia-device-plugin/config.json

符号链接目标
nvidia-device-plugin

核心设备插件
mps-control-daemon mount-shm

创建 /mps/shm tmpfs
/mps/

MPS 根目录
mps-control-daemon

管理 MPS 守护进程
GPU 1

EXCLUSIVE_PROCESS
ConfigMap

12.2 MPS 共享模式完整时序

GPU 应用 Pod nvidia-device-plugin nvidia-smi nvidia-cuda-mps-control mps-control-daemon Init Container Kubernetes GPU 应用 Pod nvidia-device-plugin nvidia-smi nvidia-cuda-mps-control mps-control-daemon Init Container Kubernetes #mermaid-svg-o3KkPM4KN0p7Iilu{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-o3KkPM4KN0p7Iilu .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-o3KkPM4KN0p7Iilu .error-icon{fill:#552222;}#mermaid-svg-o3KkPM4KN0p7Iilu .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-o3KkPM4KN0p7Iilu .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-o3KkPM4KN0p7Iilu .marker{fill:#333333;stroke:#333333;}#mermaid-svg-o3KkPM4KN0p7Iilu .marker.cross{stroke:#333333;}#mermaid-svg-o3KkPM4KN0p7Iilu svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-o3KkPM4KN0p7Iilu p{margin:0;}#mermaid-svg-o3KkPM4KN0p7Iilu .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o3KkPM4KN0p7Iilu text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-o3KkPM4KN0p7Iilu .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-o3KkPM4KN0p7Iilu .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-o3KkPM4KN0p7Iilu #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-o3KkPM4KN0p7Iilu .sequenceNumber{fill:white;}#mermaid-svg-o3KkPM4KN0p7Iilu #sequencenumber{fill:#333;}#mermaid-svg-o3KkPM4KN0p7Iilu #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-o3KkPM4KN0p7Iilu .messageText{fill:#333;stroke:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o3KkPM4KN0p7Iilu .labelText,#mermaid-svg-o3KkPM4KN0p7Iilu .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .loopText,#mermaid-svg-o3KkPM4KN0p7Iilu .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-o3KkPM4KN0p7Iilu .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-o3KkPM4KN0p7Iilu .noteText,#mermaid-svg-o3KkPM4KN0p7Iilu .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-o3KkPM4KN0p7Iilu .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o3KkPM4KN0p7Iilu .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o3KkPM4KN0p7Iilu .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-o3KkPM4KN0p7Iilu .actorPopupMenu{position:absolute;}#mermaid-svg-o3KkPM4KN0p7Iilu .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-o3KkPM4KN0p7Iilu .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-o3KkPM4KN0p7Iilu .actor-man circle,#mermaid-svg-o3KkPM4KN0p7Iilu line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-o3KkPM4KN0p7Iilu :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动 Init Container mount-shm (创建 /mps/shm tmpfs) 启动 mps-control-daemon 加载配置,初始化 NVML 创建 ResourceManager nvidia-smi -c EXCLUSIVE_PROCESS GPU 计算模式设置成功 创建 /mps/Resource/pipe 目录 创建 /mps/Resource/log 目录 nvidia-cuda-mps-control -d (启动 MPS 服务) MPS 服务已启动 set_default_device_pinned_mem_limit 0 6144M OK set_default_active_thread_percentage 25 OK 创建 /mps/.ready 启动 log tailer 启动 nvidia-device-plugin 注册设备资源(含 MPS 共享资源) 调度 GPU 应用 Pod 设置 CUDA_MPS_PIPE_DIRECTORY 通过管道连接 MPS 服务 多进程共享 GPU 资源

12.3 Config Manager 配置切换时序

nvidia-device-plugin 进程 Filesystem Main Loop SyncableConfig Informer Kubernetes API 运维人员 nvidia-device-plugin 进程 Filesystem Main Loop SyncableConfig Informer Kubernetes API 运维人员 #mermaid-svg-6QC7MyzxQoffwAzK{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-6QC7MyzxQoffwAzK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6QC7MyzxQoffwAzK .error-icon{fill:#552222;}#mermaid-svg-6QC7MyzxQoffwAzK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6QC7MyzxQoffwAzK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6QC7MyzxQoffwAzK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6QC7MyzxQoffwAzK .marker.cross{stroke:#333333;}#mermaid-svg-6QC7MyzxQoffwAzK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6QC7MyzxQoffwAzK p{margin:0;}#mermaid-svg-6QC7MyzxQoffwAzK .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6QC7MyzxQoffwAzK text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-6QC7MyzxQoffwAzK .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-6QC7MyzxQoffwAzK .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-6QC7MyzxQoffwAzK .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-6QC7MyzxQoffwAzK .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-6QC7MyzxQoffwAzK #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-6QC7MyzxQoffwAzK .sequenceNumber{fill:white;}#mermaid-svg-6QC7MyzxQoffwAzK #sequencenumber{fill:#333;}#mermaid-svg-6QC7MyzxQoffwAzK #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-6QC7MyzxQoffwAzK .messageText{fill:#333;stroke:none;}#mermaid-svg-6QC7MyzxQoffwAzK .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6QC7MyzxQoffwAzK .labelText,#mermaid-svg-6QC7MyzxQoffwAzK .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-6QC7MyzxQoffwAzK .loopText,#mermaid-svg-6QC7MyzxQoffwAzK .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-6QC7MyzxQoffwAzK .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-6QC7MyzxQoffwAzK .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-6QC7MyzxQoffwAzK .noteText,#mermaid-svg-6QC7MyzxQoffwAzK .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-6QC7MyzxQoffwAzK .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6QC7MyzxQoffwAzK .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6QC7MyzxQoffwAzK .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-6QC7MyzxQoffwAzK .actorPopupMenu{position:absolute;}#mermaid-svg-6QC7MyzxQoffwAzK .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-6QC7MyzxQoffwAzK .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-6QC7MyzxQoffwAzK .actor-man circle,#mermaid-svg-6QC7MyzxQoffwAzK line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-6QC7MyzxQoffwAzK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} kubectl label node node1 nvidia.com/device-plugin.config=sharing-config Node Update 事件 Set("sharing-config") Get() 返回 "sharing-config" updateConfigName("sharing-config") 读取 /etc/nvidia-config-manager/ 目录 验证 "sharing-config" 文件存在 EvalSymlinks(/etc/nvidia-config-manager/sharing-config) EvalSymlinks(/etc/nvidia-device-plugin/config.json) 比较路径 → 不同 → 需要更新 Remove /etc/nvidia-device-plugin/config.json Symlink → /etc/nvidia-config-manager/sharing-config findPidToSignal("nvidia-device-plugin") syscall.Kill(pid, SIGHUP) 收到 SIGHUP,重新加载配置 使用新的共享配置运行 Get() 阻塞等待下一次变更

12.4 代码依赖关系图

#mermaid-svg-hgsLPxOuKbZ4hCms{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-hgsLPxOuKbZ4hCms .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hgsLPxOuKbZ4hCms .error-icon{fill:#552222;}#mermaid-svg-hgsLPxOuKbZ4hCms .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hgsLPxOuKbZ4hCms .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hgsLPxOuKbZ4hCms .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hgsLPxOuKbZ4hCms .marker.cross{stroke:#333333;}#mermaid-svg-hgsLPxOuKbZ4hCms svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hgsLPxOuKbZ4hCms p{margin:0;}#mermaid-svg-hgsLPxOuKbZ4hCms .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster-label text{fill:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster-label span{color:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster-label span p{background-color:transparent;}#mermaid-svg-hgsLPxOuKbZ4hCms .label text,#mermaid-svg-hgsLPxOuKbZ4hCms span{fill:#333;color:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms .node rect,#mermaid-svg-hgsLPxOuKbZ4hCms .node circle,#mermaid-svg-hgsLPxOuKbZ4hCms .node ellipse,#mermaid-svg-hgsLPxOuKbZ4hCms .node polygon,#mermaid-svg-hgsLPxOuKbZ4hCms .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hgsLPxOuKbZ4hCms .rough-node .label text,#mermaid-svg-hgsLPxOuKbZ4hCms .node .label text,#mermaid-svg-hgsLPxOuKbZ4hCms .image-shape .label,#mermaid-svg-hgsLPxOuKbZ4hCms .icon-shape .label{text-anchor:middle;}#mermaid-svg-hgsLPxOuKbZ4hCms .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hgsLPxOuKbZ4hCms .rough-node .label,#mermaid-svg-hgsLPxOuKbZ4hCms .node .label,#mermaid-svg-hgsLPxOuKbZ4hCms .image-shape .label,#mermaid-svg-hgsLPxOuKbZ4hCms .icon-shape .label{text-align:center;}#mermaid-svg-hgsLPxOuKbZ4hCms .node.clickable{cursor:pointer;}#mermaid-svg-hgsLPxOuKbZ4hCms .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hgsLPxOuKbZ4hCms .arrowheadPath{fill:#333333;}#mermaid-svg-hgsLPxOuKbZ4hCms .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hgsLPxOuKbZ4hCms .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hgsLPxOuKbZ4hCms .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hgsLPxOuKbZ4hCms .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hgsLPxOuKbZ4hCms .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hgsLPxOuKbZ4hCms .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster text{fill:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms .cluster span{color:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms 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-hgsLPxOuKbZ4hCms .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hgsLPxOuKbZ4hCms rect.text{fill:none;stroke-width:0;}#mermaid-svg-hgsLPxOuKbZ4hCms .icon-shape,#mermaid-svg-hgsLPxOuKbZ4hCms .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hgsLPxOuKbZ4hCms .icon-shape p,#mermaid-svg-hgsLPxOuKbZ4hCms .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hgsLPxOuKbZ4hCms .icon-shape .label rect,#mermaid-svg-hgsLPxOuKbZ4hCms .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hgsLPxOuKbZ4hCms .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hgsLPxOuKbZ4hCms .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hgsLPxOuKbZ4hCms :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} cmd/gpu-feature-discovery
api/config/v1
internal/flags
internal/info
internal/lm
internal/resource
internal/vgpu
internal/watch
go-nvml
go-nvlib
urfave/cli
cmd/config-manager
k8s.io/client-go
procfs
cmd/mps-control-daemon
internal/rm
mps/manager
mount/mount-shm
mount-utils
mps/daemon
mps/root
mps/log-tailer
selinux
mps/device
semver
mps/options

12.5 关键设计模式总结

设计模式 应用位置 说明
函数式选项 mps/options.go WithConfig() 返回闭包配置 manager
空对象模式 mps/manager.go nullManager 在非 MPS 模式下返回空 Daemon 列表
生产者-消费者 config-manager/main.go SyncableConfigsync.Cond 实现配置变更通知
Informer 模式 config-manager/main.go 使用 K8s Informer 监听 Node 变化,避免轮询
管道通信 mps/daemon.go EchoPipeToControl 通过 io.Pipe 与 MPS 控制进程交互
信号驱动 所有三个命令 使用 syscall.Signal 处理 SIGHUP/SIGINT/SIGTERM
类型别名 mps/device.go mpsDevicerm.Device 的别名,附加 MPS 专有方法

12.3 信号处理对比

命令 SIGHUP SIGINT/SIGTERM/SIGQUIT
GFD 重新加载配置 + 重启 优雅退出
Config Manager 无显式处理 无显式处理
MPS Daemon 停止所有 daemon → 重启 停止所有 daemon → 退出

附录:完整文件索引

文件路径 行数 核心职责
cmd/gpu-feature-discovery/main.go ~270 GFD 入口、CLI 定义、标注循环
cmd/config-manager/main.go ~310 配置管理入口、Informer、符号链接管理
cmd/mps-control-daemon/main.go ~160 MPS Daemon 入口、生命周期管理
cmd/mps-control-daemon/mps/daemon.go ~210 MPS Daemon 实现(启停、控制、限制)
cmd/mps-control-daemon/mps/device.go ~45 MPS 设备校验(副本数限制)
cmd/mps-control-daemon/mps/log-tailer.go ~50 日志跟踪器
cmd/mps-control-daemon/mps/manager.go ~95 Daemon 管理器(创建 Daemon 集合)
cmd/mps-control-daemon/mps/options.go ~20 函数式选项
cmd/mps-control-daemon/mps/root.go ~50 路径管理
cmd/mps-control-daemon/mount/mount-shm.go ~90 tmpfs 共享内存挂载

本文档基于 NVIDIA k8s-device-plugin v0.19.3 源码,严格依据代码分析生成。

相关推荐
熊猫钓鱼>_>1 小时前
ArkTS 基础入门:从零搭建第一个交互页面
华为·架构·交互·ts·harmonyos·arkts·鸿蒙
QN1幻化引擎1 小时前
把 意识评测做成了一场"非侵入实验":不碰生产代码,分数反而更真了
人工智能·算法·架构
带娃的IT创业者1 小时前
单文件架构的极致美学:深入解析 Bento 的 HTML 幻灯片技术实现
前端·架构·html·web开发·bento·单文件架构
JL152 小时前
Java+Go 混合架构怎么搭?收官 50 道面试题 + 学习路线
java·架构·golang
微三云 - 廖会灵 (私域系统开发)2 小时前
企业私域商城系统架构选型分析:单体模板VS微服务自研架构
微服务·架构·系统架构
AINative软件工程3 小时前
LLM 应用的 Graceful Degradation 工程实践:5 层降级策略让你的 AI 功能永不完全崩溃
架构·llm·ai编程
池以遇3 小时前
云原生——LVS
运维·云原生·lvs
2601_954706495 小时前
基于 ARM 虚拟化的云手机架构选型与 Python 自动化实战——2026 年 8 月为何我推荐傲晨云手机
arm开发·智能手机·架构
FII工业富联科技服务12 小时前
工业设备运维如何Agentic化?拆解Factory Brain的设备运维架构
大数据·运维·人工智能·架构·制造