hook/ — 超深度逐行分析

两个文件:hook/main.go (67行) + hook/process/process.go (413行) = 480行
核心职责:OCI Prestart Hook实现,在容器namespace已创建但作业未启动时执行设备挂载


一、模块定位

1.1 业务职责

hook/ 模块是 OCI Prestart Hook 的实现。当 runc 在容器创建过程中触发 Prestart Hook 时,此模块被调用。此时容器的 namespace 已创建,但容器内的作业尚未启动,是一个安全的时间窗口,可以:

  1. 解析容器配置 — 从stdin读取OCI State,获取容器PID和rootfs路径
  2. 读取挂载配置 — 从 /etc/ascend-docker-runtime.d/ 读取 .list 配置文件
  3. 添加UB挂载 — 自动添加HCCL根信息和拓扑目录
  4. 解析运行时选项 — NODRV(无驱动模式)、VIRTUAL(虚拟设备模式)
  5. 解析软链接模式 — 是否允许通过软链接挂载
  6. 执行CLIsyscall.Exec 替换为 ascend-docker-cli(C语言程序)执行实际挂载

1.2 在系统中的位置

时间线

Docker调用
ascend-docker-runtime

runtime修改config.json
注入Hook+设备

runc create
创建namespace

容器状态: created

runc触发Prestart Hook
执行ascend-docker-hook

hook/main.go启动

hook解析配置

syscall.Exec
替换为ascend-docker-cli

CLI挂载设备/文件/目录
到容器namespace

容器状态: running
NPU可用

1.3 Prestart Hook 时序

ascend-docker-cli hook/process hook/main.go runc ascend-docker-cli hook/process hook/main.go runc alt [无NPU设备] [有NPU设备] exec ascend-docker-hook stdin: OCI State JSON panic恢复 InitLogModule 日志初始化 GetLogPrefix 获取uid+tty StringChecker 参数校验 DoPrestartHook() getContainerConfig 从stdin读取OCI State 解析config.json 检查ASCEND_VISIBLE_DEVICES 返回nil(不处理) parseMounts 解析挂载配置名 readConfigsOfDir 读取.list文件 addUBMount 添加HCCL+Topo parseRuntimeOptions parseSoftLinkMode 查找ascend-docker-cli 组装CLI参数 syscall.Exec 替换进程 挂载文件/目录到容器namespace

二、hook/main.go 逐行解析(67行)

2.1 导入

package main

import (
    "context"
    "fmt"
    "log"
    "os"
    "strings"

    "ascend-common/api"
    "ascend-common/common-utils/hwlog"
    "ascend-docker-runtime/hook/process"
    "ascend-docker-runtime/mindxcheckutils"
)

const (
    loggingPrefix = api.AscendDockerHook
)
导入项 作用
api 提供 AscendDockerHook 常量(日志前缀)
process Hook处理包
mindxcheckutils 安全校验

loggingPrefix — 设置标准log的前缀,用于区分不同组件的日志。

2.2 main函数

func main() {
    defer func() {
        if err := recover(); err != nil {
            log.Fatal(err)
        }
    }()
    log.SetPrefix(loggingPrefix)
  • panic恢复:与runtime/main.go相同的设计,defer recover捕获panic
  • log.SetPrefix(loggingPrefix) — 设置标准log前缀为 api.AscendDockerHook(如"[ascend-docker-hook]")
    ctx, _ := context.WithCancel(context.Background())
    if err := process.InitLogModule(ctx); err != nil {
        log.Fatal(err)
    }
    logPrefixWords, err := mindxcheckutils.GetLogPrefix()
    if err != nil {
        log.Fatal(err)
    }
    defer func() {
        if err := mindxcheckutils.ChangeRuntimeLogMode("hook-run-"); err != nil {
            fmt.Println("defer changeFileMode function failed")
        }
    }()
  • 与runtime/main.go的模式完全一致
    1. 创建context
    2. 初始化hwlog日志
    3. 获取日志前缀(uid+tty)
    4. defer收紧日志权限(前缀为"hook-run-“,区别于runtime的"runtime-run-”)
    hwlog.RunLog.Infof("%v docker hook starting, try to setup container", logPrefixWords)
    if !mindxcheckutils.StringChecker(strings.Join(os.Args, " "), 0,
        process.MaxCommandLength, mindxcheckutils.DefaultWhiteList+" ") {
        hwlog.RunLog.Errorf("%v docker hook failed", logPrefixWords)
        log.Fatal("command error")
    }
    if err := process.DoPrestartHook(); err != nil {
        hwlog.RunLog.Errorf("%v docker hook failed: %#v", logPrefixWords, err)
        log.Fatal(fmt.Errorf("failed in runtime.doProcess: %#v", err))
    }
}

逐行解析

  1. 记录启动日志
  2. 参数安全校验:与runtime/main.go相同的StringChecker白名单校验
  3. 调用 process.DoPrestartHook() — 核心处理
  4. 错误处理:记录hwlog错误 + log.Fatal终止

设计意图:hook/main.go与runtime/main.go结构几乎完全相同,遵循统一的入口模式:panic恢复→日志→前缀→安全校验→核心逻辑。

失败

成功

失败

成功

hook main启动

defer panic恢复

SetPrefix日志前缀

创建context

InitLogModule日志初始化

GetLogPrefix

defer ChangeRuntimeLogMode
hook-run-前缀

StringChecker参数校验

log.Fatal

DoPrestartHook核心处理

log.Fatal

正常退出


三、hook/process/process.go 逐行解析(413行)

3.1 常量定义

const (
    runLogPath             = api.HookRunLogPath
    ascendRuntimeOptions   = api.AscendRuntimeOptionsEnv
    ascendRuntimeMounts    = api.AscendRuntimeMountsEnv
    ascendVisibleDevices   = api.AscendVisibleDevicesEnv
    ascendAllowLink        = api.AscendAllowLinkEnv
    ascendDockerCli        = "ascend-docker-cli"
    defaultAscendDockerCli = "/usr/local/bin/ascend-docker-cli"
    configDir              = api.RunTimeDConfigPath
    baseConfig             = "base"
    configFileSuffix       = "list"
    hcclRootInfo           = "/etc/hccl_rootinfo.json"
    topoDirPath            = "/usr/local/Ascend/driver/topo"

    kvPairSize       = 2
    MaxCommandLength = 65535
)
常量 用途
runLogPath api定义路径 Hook日志文件路径
ascendRuntimeMounts ASCEND_RUNTIME_MOUNTS 挂载配置名环境变量
ascendAllowLink ASCEND_ALLOW_LINK 软链接模式环境变量
ascendDockerCli "ascend-docker-cli" CLI程序文件名
configDir /etc/ascend-docker-runtime.d/ 挂载配置文件目录
baseConfig "base" 默认基础配置名
configFileSuffix "list" 配置文件后缀(如base.list
hcclRootInfo /etc/hccl_rootinfo.json HCCL集合通信根信息
topoDirPath /usr/local/Ascend/driver/topo NPU拓扑目录

3.2 变量定义

var (
    limitReader                = io.LimitReader(os.Stdin, limiter.DefaultDataLimit)
    doExec                     = syscall.Exec
    ascendDockerCliName        = ascendDockerCli
    defaultAscendDockerCliName = defaultAscendDockerCli
)

var validRuntimeOptions = [...]string{
    "NODRV",
    "VIRTUAL",
}
变量 用途
limitReader 限制stdin读取大小,防止超大输入攻击
doExec syscall.Exec的变量引用(可被测试mock)
validRuntimeOptions 合法运行时选项白名单:NODRV(无驱动模式)、VIRTUAL(虚拟设备模式)

设计意图limitReader 防止恶意容器提供超大的OCI State JSON导致内存耗尽。limiter.DefaultDataLimit 是ascend-common库定义的合理上限。

3.3 containerConfig 结构体

type containerConfig struct {
    Pid    int
    Rootfs string
    Env    []string
}

containerConfig

+int Pid "容器进程PID(在Host命名空间中)"

+string Rootfs "容器根文件系统路径"

+[]string Env "容器环境变量列表"

  • Pid — 容器进程的Host PID,CLI用此PID进入容器的namespace
  • Rootfs — 容器根文件系统在Host上的路径,CLI需要此路径来挂载文件/目录
  • Env — 容器环境变量,用于解析ASCEND_*变量

3.4 InitLogModule

func InitLogModule(ctx context.Context) error {
    const backups = 2
    const logMaxAge = 365
    const fileMaxSize = 2
    runLogConfig := hwlog.LogConfig{
        LogFileName: runLogPath,
        LogLevel:    0,
        MaxBackups:  backups,
        MaxAge:      logMaxAge,
        OnlyToFile:  true,
        FileMaxSize: fileMaxSize,
    }
    if err := hwlog.InitRunLogger(&runLogConfig, ctx); err != nil {
        fmt.Printf("log init failed, error is %v", err)
        return err
    }
    return nil
}

与runtime/process的InitLogModule完全相同的设计,仅日志路径不同(使用api.HookRunLogPath)。

3.5 parseMounts — 挂载配置名解析

func parseMounts(mounts string) []string {
    if mounts == "" {
        return []string{baseConfig}
    }
    const maxMountLength = 128
    if len(mounts) > maxMountLength {
        return []string{baseConfig}
    }

    mountConfigs := make([]string, 0)
    for _, m := range strings.Split(mounts, ",") {
        m = strings.TrimSpace(m)
        m = strings.ToLower(m)
        mountConfigs = append(mountConfigs, m)
    }

    return mountConfigs
}

逐行解析

  1. 如果空 → 返回默认的 ["base"]
  2. 长度超过128 → 降级为 ["base"](安全限制)
  3. 按逗号分割(如 "base,custom1,custom2"["base","custom1","custom2"]
  4. 每个配置名做 TrimSpace + ToLower 标准化

设计意图ASCEND_RUNTIME_MOUNTS=base,custom 指定要读取哪些.list配置文件。每个配置文件对应一组需要挂载的文件/目录列表。

3.6 parseRuntimeOptions — 运行时选项解析

func isRuntimeOptionValid(option string) bool {
    for _, validOption := range validRuntimeOptions {
        if option == validOption {
            return true
        }
    }
    return false
}

func parseRuntimeOptions(runtimeOptions string) ([]string, error) {
    parsedOptions := make([]string, 0)

    if runtimeOptions == "" {
        return parsedOptions, nil
    }
    const maxLength = 128
    if len(runtimeOptions) > maxLength {
        return nil, fmt.Errorf("invalid runtime option, the length exceeds 128 characters")
    }

    for _, option := range strings.Split(runtimeOptions, ",") {
        option = strings.TrimSpace(option)
        if !isRuntimeOptionValid(option) {
            return nil, fmt.Errorf("invalid runtime option of invalid input value")
        }
        parsedOptions = append(parsedOptions, option)
    }

    return parsedOptions, nil
}

逐行解析

  1. 空字符串 → 返回空列表
  2. 长度超过128 → 拒绝
  3. 按逗号分割
  4. 每个选项TrimSpace后用白名单校验
  5. 只允许"NODRV"或"VIRTUAL"

NODRV模式说明:无驱动模式,只挂载设备文件但不加载驱动,用于调试或特殊场景。

3.7 parseSoftLinkMode — 软链接模式解析

func parseSoftLinkMode(allowLink string) (string, error) {
    if allowLink == "True" {
        return "True", nil
    }
    if allowLink == "" || allowLink == "False" {
        return "False", nil
    }
    return "", fmt.Errorf("invalid soft link option")
}
输入 输出 说明
"True" "True" 允许软链接挂载
"" "False" 默认禁止
"False" "False" 显式禁止
其他 error 非法值

设计意图:软链接挂载有安全风险(可能指向任意路径),默认禁止,需要显式开启。

3.8 getContainerConfig — 从stdin读取容器配置

var getContainerConfig = func() (*containerConfig, error) {
    state := new(specs.State)
    decoder := json.NewDecoder(limitReader)

    if err := decoder.Decode(state); err != nil {
        return nil, fmt.Errorf("failed to parse the container's state")
    }

    configPath := path.Join(state.Bundle, "config.json")
    if _, err := mindxcheckutils.RealFileChecker(configPath, true, true, mindxcheckutils.DefaultSize); err != nil {
        return nil, err
    }

    ociSpec, err := parseOciSpecFile(configPath)
    if err != nil {
        return nil, fmt.Errorf("failed to parse OCI spec: %v", err)
    }
    if len(ociSpec.Process.Env) > MaxCommandLength {
        return nil, fmt.Errorf("too many items in spec file")
    }
    // when use ctr->containerd. the rootfs in config.json is a relative path
    rfs := ociSpec.Root.Path
    if !filepath.IsAbs(rfs) {
        rfs = path.Join(state.Bundle, ociSpec.Root.Path)
    }

    ret := &containerConfig{
        Pid:    state.Pid,
        Rootfs: rfs,
        Env:    ociSpec.Process.Env,
    }

    return ret, nil
}

逐行解析

  1. specs.State — OCI State结构体,包含 Pid(容器进程PID)、Bundle(bundle目录路径)、ID(容器ID)
  2. limitReader — 限制stdin读取大小(防DoS)
  3. json.NewDecoder(limitReader).Decode(state) — 从stdin解析OCI State JSON
  4. 构造config.json路径:state.Bundle + "/config.json"
  5. RealFileChecker(configPath, true, true, ...) — 安全校验(checkParent=true, allowLink=true)
  6. parseOciSpecFile — 解析config.json获取OCI Spec
  7. 环境变量数量校验(≤65535)
  8. rootfs相对路径处理:当使用containerd(ctr命令)时,rootfs可能是相对路径,需要拼接Bundle路径

OCI State JSON格式

{
    "ociVersion": "1.0.0",
    "id": "container-id",
    "pid": 12345,
    "bundle": "/path/to/bundle",
    "status": "created"
}

getContainerConfig

从stdin读取
OCI State JSON

解析state.Pid
state.Bundle

构造config.json路径
Bundle+/config.json

RealFileChecker安全校验

parseOciSpecFile
解析config.json

提取Env列表

rootfs是绝对路径?

直接使用Root.Path

拼接Bundle+Root.Path
containerd兼容

构造containerConfig

返回Pid+Rootfs+Env

3.9 readMountConfig — 读取单个.list配置文件

func readMountConfig(dir string, name string) ([]string, []string, error) {
    configFileName := fmt.Sprintf("%s.%s", name, configFileSuffix)
    baseConfigFilePath, err := filepath.Abs(filepath.Join(dir, configFileName))
    if err != nil {
        return nil, nil, fmt.Errorf("failed to assemble base config file path: %v", err)
    }

    fileInfo, err := os.Stat(baseConfigFilePath)
    if _, err := mindxcheckutils.RealFileChecker(baseConfigFilePath, true, false,
        mindxcheckutils.DefaultSize); err != nil {
        return nil, nil, err
    }
    if err != nil {
        return nil, nil, fmt.Errorf("cannot stat base configuration file %s : %v", baseConfigFilePath, err)
    }

    if !fileInfo.Mode().IsRegular() {
        return nil, nil, fmt.Errorf("base configuration file damaged because is not a regular file")
    }

    f, err := os.Open(baseConfigFilePath)
    if err != nil {
        return nil, nil, fmt.Errorf("failed to open base configuration file %s: %v", baseConfigFilePath, err)
    }
    defer f.Close()

    fileMountList, dirMountList := make([]string, 0), make([]string, 0)
    const maxEntryNumber = 128
    entryCount := 0
    scanner := bufio.NewScanner(f)
    for scanner.Scan() {
        mountPath := scanner.Text()
        entryCount = entryCount + 1
        if entryCount > maxEntryNumber {
            return nil, nil, fmt.Errorf("mount list too long")
        }
        absMountPath, err := filepath.Abs(mountPath)
        if err != nil {
            continue
        }
        mountPath = absMountPath

        stat, err := os.Stat(mountPath)
        if err != nil {
            continue
        }

        if stat.Mode().IsRegular() {
            fileMountList = append(fileMountList, mountPath)
        } else if stat.Mode().IsDir() {
            dirMountList = append(dirMountList, mountPath)
        }
    }

    return fileMountList, dirMountList, nil
}

逐行解析

  1. 构造配置文件路径:/etc/ascend-docker-runtime.d/base.list
  2. 安全校验:RealFileChecker(防软链接攻击、文件大小限制)
  3. 常规文件检查:!fileInfo.Mode().IsRegular() 拒绝非普通文件
  4. 打开文件,defer关闭
  5. 逐行扫描(bufio.Scanner):
    • 每行是一个文件/目录的绝对路径
    • 最大128行(安全限制)
    • 转为绝对路径
    • os.Stat 检查存在性
    • 根据类型分类:普通文件→fileMountList,目录→dirMountList
  6. 错误容忍策略:任何单行解析失败都continue跳过

base.list 文件示例

/usr/local/Ascend/driver/lib64/libdcmi.so
/usr/local/Ascend/driver/lib64/libascendcl.so
/usr/local/Ascend/driver/lib64/
/usr/local/Ascend/addins/

不存在

存在

普通文件

目录

其他

readMountConfig
dir+name

构造路径
dir/name.list

RealFileChecker安全校验

检查是否普通文件

打开文件

bufio.Scanner逐行扫描

还有下一行?

entryCount > 128?

返回错误: too long

filepath.Abs转绝对路径

os.Stat检查存在性

文件类型?

加入fileMountList

加入dirMountList

返回fileMountList
dirMountList

3.10 readConfigsOfDir — 读取多个配置文件

func readConfigsOfDir(dir string, configs []string) ([]string, []string, error) {
    fileInfo, err := os.Stat(dir)
    if err != nil {
        return nil, nil, fmt.Errorf("cannot stat configuration directory %s : %v", dir, err)
    }

    if !fileInfo.Mode().IsDir() {
        return nil, nil, fmt.Errorf("%s should be a dir for docker runtime, but now it is not", dir)
    }

    fileMountList := make([]string, 0)
    dirMountList := make([]string, 0)

    for _, config := range configs {
        fileList, dirList, err := readMountConfig(dir, config)
        if err != nil {
            return nil, nil, fmt.Errorf("failed to process config %s: %v", config, err)
        }

        fileMountList = append(fileMountList, fileList...)
        dirMountList = append(dirMountList, dirList...)
    }

    return fileMountList, dirMountList, nil
}

逐行解析

  1. 校验配置目录存在且确实是目录
  2. 遍历配置名列表(如 ["base", "custom"]
  3. 对每个配置名调用 readMountConfig 读取对应的.list文件
  4. 合并所有配置的文件挂载列表和目录挂载列表

设计意图:支持多个.list配置文件的组合,实现声明式挂载配置。例如ASCEND_RUNTIME_MOUNTS=base,vnpu会读取base.listvnpu.list

3.11 addUBMount — UB挂载自动添加

func addUBMount(fileMountList []string, dirMountList []string) ([]string, []string) {
    ubMountItems := []string{
        hcclRootInfo,
        topoDirPath,
    }
    for _, mountItem := range ubMountItems {
        mountItemInfo, err := os.Stat(mountItem)
        if err != nil {
            hwlog.RunLog.Debugf("%s may not exists, error: %v", mountItem, err)
            continue
        }
        if mountItemInfo.Mode().IsDir() {
            dirMountList = append(dirMountList, mountItem)
            continue
        }
        fileMountList = append(fileMountList, mountItem)
    }

    return fileMountList, dirMountList
}

逐行解析

  1. 定义需要自动添加的UB相关挂载项:
    • /etc/hccl_rootinfo.json — HCCL集合通信根信息(文件)
    • /usr/local/Ascend/driver/topo — NPU拓扑信息(目录)
  2. 对每项检查是否存在:
    • 不存在 → Debug日志记录,跳过(非错误)
    • 是目录 → 追加到dirMountList
    • 是文件 → 追加到fileMountList

设计意图:UB(Ultra-Bandwidth)相关的HCCL和拓扑信息是NPU通信的基础设施,应自动挂载(如果存在),无需用户手动配置。

3.12 getArgs — 组装CLI参数

func getArgs(cliPath string, containerConfig *containerConfig, fileMountList []string,
    dirMountList []string, allowLink string) []string {
    args := append([]string{cliPath},
        "--allow-link", allowLink, "--pid", fmt.Sprintf("%d", containerConfig.Pid),
        "--rootfs", containerConfig.Rootfs)
    for _, filePath := range fileMountList {
        args = append(args, "--mount-file", filePath)
    }
    for _, dirPath := range dirMountList {
        args = append(args, "--mount-dir", dirPath)
    }
    return args
}

生成的CLI参数格式

ascend-docker-cli \
    --allow-link False \
    --pid 12345 \
    --rootfs /var/lib/docker/overlay2/xxx/merged \
    --mount-file /usr/local/Ascend/driver/lib64/libdcmi.so \
    --mount-file /etc/hccl_rootinfo.json \
    --mount-dir /usr/local/Ascend/driver/lib64/ \
    --mount-dir /usr/local/Ascend/driver/topo \
    --options VIRTUAL
参数 用途
--allow-link 是否允许软链接挂载(True/False)
--pid 容器进程PID,CLI用此PID进入容器namespace
--rootfs 容器根文件系统路径
--mount-file 需要挂载的文件(可重复多个)
--mount-dir 需要挂载的目录(可重复多个)
--options 运行时选项(NODRV/VIRTUAL)

3.13 DoPrestartHook — 核心处理函数

func DoPrestartHook() error {
    containerConfig, err := getContainerConfig()
    if err != nil {
        return fmt.Errorf("failed to get container config: %#v", err)
    }

    if visibleDevices := getValueByKey(containerConfig.Env, ascendVisibleDevices); visibleDevices == "" {
        return nil
    }

    mountConfigs := parseMounts(getValueByKey(containerConfig.Env, ascendRuntimeMounts))

    fileMountList, dirMountList, err := readConfigsOfDir(configDir, mountConfigs)
    if err != nil {
        return fmt.Errorf("failed to read configuration from config directory: %#v", err)
    }
    // adapt a5 ub mount
    fileMountList, dirMountList = addUBMount(fileMountList, dirMountList)
    hwlog.RunLog.Infof("mount files: [%v]", strings.Join(fileMountList, ", "))
    hwlog.RunLog.Infof("mount directories: [%v]", strings.Join(dirMountList, ", "))

    parsedOptions, err := parseRuntimeOptions(getValueByKey(containerConfig.Env, ascendRuntimeOptions))
    if err != nil {
        return fmt.Errorf("failed to parse runtime options: %#v", err)
    }

    allowLink, err := parseSoftLinkMode(getValueByKey(containerConfig.Env, ascendAllowLink))
    if err != nil {
        return fmt.Errorf("failed to parse soft link mode: %#v", err)
    }

    currentExecPath, err := os.Executable()
    if err != nil {
        return fmt.Errorf("cannot get the path of docker-hook: %#v", err)
    }

    cliPath := path.Join(path.Dir(currentExecPath), ascendDockerCliName)
    if _, err = os.Stat(cliPath); err != nil {
        return fmt.Errorf("cannot find docker-cli executable file at %s: %#v", cliPath, err)
    }
    if _, err := mindxcheckutils.RealFileChecker(cliPath, true, false, mindxcheckutils.DefaultSize); err != nil {
        return err
    }
    args := getArgs(cliPath, containerConfig, fileMountList, dirMountList, allowLink)
    if len(parsedOptions) > 0 {
        args = append(args, "--options", strings.Join(parsedOptions, ","))
    }
    hwlog.RunLog.Info("docker hook success, will start cli")
    if err := mindxcheckutils.ChangeRuntimeLogMode("hook-run-"); err != nil {
        return err
    }
    if err := doExec(cliPath, args, os.Environ()); err != nil {
        return fmt.Errorf("failed to exec docker-cli %v: %v", args, err)
    }
    return nil
}

完整执行流程

  1. getContainerConfig() — 从stdin读取OCI State + config.json
  2. 检查 ASCEND_VISIBLE_DEVICES — 如果为空,直接返回(非NPU容器)
  3. parseMounts — 解析 ASCEND_RUNTIME_MOUNTS 获取挂载配置名列表
  4. readConfigsOfDir — 读取 /etc/ascend-docker-runtime.d/ 下的.list文件
  5. addUBMount — 自动添加HCCL和Topo挂载
  6. parseRuntimeOptions — 解析 ASCEND_RUNTIME_OPTIONS
  7. parseSoftLinkMode — 解析 ASCEND_ALLOW_LINK
  8. 查找 ascend-docker-cli — 在hook同目录下
  9. RealFileChecker — 安全校验CLI文件
  10. getArgs — 组装CLI参数
  11. 如果有运行时选项 → 追加 --options 参数
  12. ChangeRuntimeLogMode — 收紧日志权限
  13. doExec (syscall.Exec) — 替换进程为ascend-docker-cli

成功

失败

DoPrestartHook

getContainerConfig
stdin读取OCI State

ASCEND_VISIBLE_DEVICES
为空?

返回nil
非NPU容器不处理

parseMounts
解析挂载配置名

readConfigsOfDir
读取.list文件

addUBMount
添加HCCL+Topo

parseRuntimeOptions
解析NODRV/VIRTUAL

parseSoftLinkMode
解析软链接模式

os.Executable
获取hook路径

构造CLI路径
同目录下ascend-docker-cli

RealFileChecker
安全校验CLI

getArgs
组装CLI参数

有运行时选项?

追加 --options参数

ChangeRuntimeLogMode
收紧日志权限

syscall.Exec
替换进程为CLI

进程变为CLI
不会返回

返回错误


四、关键设计决策

决策 原因 实现
stdin读取OCI State OCI Hook规范要求通过stdin传递State io.LimitReader(os.Stdin, ...)
限制stdin读取大小 防止超大JSON导致内存耗尽 limiter.DefaultDataLimit
.list配置文件 声明式挂载,非硬编码 /etc/ascend-docker-runtime.d/base.list
最大128行限制 防止配置文件过大 maxEntryNumber = 128
自动添加UB挂载 HCCL/Topo是NPU通信基础 addUBMount 检测存在即添加
syscall.Exec替换进程 Hook执行完毕后CLI需要继续运行 doExec = syscall.Exec
环境变量解析顺序 GetValueByKey从前往后 第一个匹配的值生效
Logo

鲲鹏昇腾开发者社区是面向全社会开放的“联接全球计算开发者,聚合华为+生态”的社区,内容涵盖鲲鹏、昇腾资源,帮助开发者快速获取所需的知识、经验、软件、工具、算力,支撑开发者易学、好用、成功,成为核心开发者。

更多推荐