runtime/process/process.go --- 超深度逐行分析
文件行数:807行 | 核心职责:拦截create命令、修改OCI Spec、注入NPU设备与Hook
这是整个项目最核心、最复杂的文件
一、模块定位
1.1 业务职责
runtime/process/process.go 是 ascend-docker-runtime 的核心处理引擎。当 Docker 调用 ascend-docker-runtime 时,此文件负责:
- 解析命令行参数 --- 提取
--bundle路径和create命令 - 读取OCI Spec --- 解析容器的 config.json
- 检测NPU需求 --- 解析
ASCEND_VISIBLE_DEVICES环境变量 - 选择NPU驱动 --- V1(旧版910/310)或V2(新版910B/A5)
- 注入Hook --- 向Spec添加Prestart和Poststop Hook
- 创建虚拟设备 --- 如果是VIRTUAL模式,切分vNPU
- 挂载设备 --- 向Spec添加davinci设备+管理设备+UB设备
- 写回Spec --- 将修改后的config.json写回磁盘
- 执行runc --- 透传给runc完成容器创建
1.2 在系统中的位置
#mermaid-svg-8etg4kidEhZVcS5X{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-8etg4kidEhZVcS5X .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8etg4kidEhZVcS5X .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8etg4kidEhZVcS5X .error-icon{fill:#552222;}#mermaid-svg-8etg4kidEhZVcS5X .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8etg4kidEhZVcS5X .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8etg4kidEhZVcS5X .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8etg4kidEhZVcS5X .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8etg4kidEhZVcS5X .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8etg4kidEhZVcS5X .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8etg4kidEhZVcS5X .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8etg4kidEhZVcS5X .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8etg4kidEhZVcS5X .marker.cross{stroke:#333333;}#mermaid-svg-8etg4kidEhZVcS5X svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8etg4kidEhZVcS5X p{margin:0;}#mermaid-svg-8etg4kidEhZVcS5X .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8etg4kidEhZVcS5X .cluster-label text{fill:#333;}#mermaid-svg-8etg4kidEhZVcS5X .cluster-label span{color:#333;}#mermaid-svg-8etg4kidEhZVcS5X .cluster-label span p{background-color:transparent;}#mermaid-svg-8etg4kidEhZVcS5X .label text,#mermaid-svg-8etg4kidEhZVcS5X span{fill:#333;color:#333;}#mermaid-svg-8etg4kidEhZVcS5X .node rect,#mermaid-svg-8etg4kidEhZVcS5X .node circle,#mermaid-svg-8etg4kidEhZVcS5X .node ellipse,#mermaid-svg-8etg4kidEhZVcS5X .node polygon,#mermaid-svg-8etg4kidEhZVcS5X .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8etg4kidEhZVcS5X .rough-node .label text,#mermaid-svg-8etg4kidEhZVcS5X .node .label text,#mermaid-svg-8etg4kidEhZVcS5X .image-shape .label,#mermaid-svg-8etg4kidEhZVcS5X .icon-shape .label{text-anchor:middle;}#mermaid-svg-8etg4kidEhZVcS5X .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8etg4kidEhZVcS5X .rough-node .label,#mermaid-svg-8etg4kidEhZVcS5X .node .label,#mermaid-svg-8etg4kidEhZVcS5X .image-shape .label,#mermaid-svg-8etg4kidEhZVcS5X .icon-shape .label{text-align:center;}#mermaid-svg-8etg4kidEhZVcS5X .node.clickable{cursor:pointer;}#mermaid-svg-8etg4kidEhZVcS5X .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8etg4kidEhZVcS5X .arrowheadPath{fill:#333333;}#mermaid-svg-8etg4kidEhZVcS5X .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8etg4kidEhZVcS5X .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8etg4kidEhZVcS5X .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8etg4kidEhZVcS5X .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8etg4kidEhZVcS5X .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8etg4kidEhZVcS5X .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8etg4kidEhZVcS5X .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8etg4kidEhZVcS5X .cluster text{fill:#333;}#mermaid-svg-8etg4kidEhZVcS5X .cluster span{color:#333;}#mermaid-svg-8etg4kidEhZVcS5X 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-8etg4kidEhZVcS5X .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8etg4kidEhZVcS5X rect.text{fill:none;stroke-width:0;}#mermaid-svg-8etg4kidEhZVcS5X .icon-shape,#mermaid-svg-8etg4kidEhZVcS5X .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8etg4kidEhZVcS5X .icon-shape p,#mermaid-svg-8etg4kidEhZVcS5X .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8etg4kidEhZVcS5X .icon-shape .label rect,#mermaid-svg-8etg4kidEhZVcS5X .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8etg4kidEhZVcS5X .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8etg4kidEhZVcS5X .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8etg4kidEhZVcS5X :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 调用链
runtime/main.go
DoProcess
入口函数
getArgs
解析参数
modifySpecFile
修改Spec
readSpecFile
读取config.json
processDevicesAndHooks
设备+Hook处理
writeSpecFile
写回config.json
execRunc
执行runc
checkVisibleDevice
检测NPU需求
GetMatchingNpuWorker
选择驱动
addHook
注入Hook
addDevice
挂载设备
二、常量与变量定义(第28-110行)
2.1 常量定义
go
const (
runLogPath = api.RunTimeRunLogPath
hookDefaultFilePath = "/usr/local/bin/ascend-docker-hook"
MaxCommandLength = 65535
hookCli = "ascend-docker-hook"
destroyHookCli = "ascend-docker-destroy"
dockerRuncFile = "docker-runc"
runcFile = "runc"
envLength = 2
kvPairSize = 2
borderNum = 2
useAscendDocker = api.AscendDockerRuntimeEnv + "=True"
ascendVisibleDevices = api.AscendVisibleDevicesEnv
ascendRuntimeOptions = api.AscendRuntimeOptionsEnv
void = "void"
)
| 常量 | 值 | 用途 |
|---|---|---|
runLogPath |
api定义的路径 | 日志文件路径 |
hookDefaultFilePath |
/usr/local/bin/ascend-docker-hook |
Hook默认路径(未使用,仅声明) |
MaxCommandLength |
65535 | 命令行参数最大长度,用于安全校验 |
hookCli |
"ascend-docker-hook" |
Hook程序文件名 |
destroyHookCli |
"ascend-docker-destroy" |
Destroy程序文件名 |
dockerRuncFile |
"docker-runc" |
Docker版runc文件名 |
runcFile |
"runc" |
标准runc文件名 |
envLength |
2 | 环境变量key=value对的长度 |
kvPairSize |
2 | SplitN的分段数 |
borderNum |
2 | 设备范围"0-3"的边界数(左+右) |
useAscendDocker |
"ASCEND_DOCKER_RUNTIME=True" |
标识容器使用ascend-docker-runtime |
ascendVisibleDevices |
"ASCEND_VISIBLE_DEVICES" |
NPU设备可见性环境变量名 |
ascendRuntimeOptions |
"ASCEND_RUNTIME_OPTIONS" |
运行时选项环境变量名 |
void |
"void" |
表示不需要挂载NPU |
2.2 变量定义
go
var (
hookCliPath = hookCli
hookDefaultFile = hookDefaultFilePath
dockerRuncName = dockerRuncFile
runcName = runcFile
deviceRegx = fmt.Sprintf(`^(?:%s(%s|%s|%s|%s)|%s)-(\d+)$`, api.Ascend, api.Ascend910No,
api.Ascend310BNo, api.Ascend310PNo, api.Ascend310No, api.NPULowerCase)
ascend910A5ManagerDevices = []string{hisiHdc}
defaultManagerDevices = []string{devmmSvm, hisiHdc}
managerDevicesMap = map[string][]string{
Ascend910A5: ascend910A5ManagerDevices,
}
)
deviceRegx 正则表达式解析:
正则 ^(?:Ascend(910|310B|310P|310)|npu)-(\d+)$ 用于匹配设备名格式:
| 匹配格式 | 示例 | 说明 |
|---|---|---|
Ascend910-N |
Ascend910-0 |
910芯片第N个设备 |
Ascend310B-N |
Ascend310B-1 |
310B芯片 |
Ascend310P-N |
Ascend310P-0 |
310P芯片 |
Ascend310-N |
Ascend310-2 |
310芯片 |
npu-N |
npu-0 |
通用NPU命名 |
设计意图:使用变量(非常量)是因为这些值可能在测试中被覆盖。
2.3 设备类型常量(第90-130行)
go
const (
Atlas200ISoc = "Atlas 200I SoC A1"
Atlas200 = "Atlas 200 Model 3000"
Ascend310 = api.Ascend310
Ascend310P = api.Ascend310P
Ascend310B = api.Ascend310B
Ascend910 = api.Ascend910
Ascend910A5 = api.Ascend910A5
ascend = api.Ascend
npu = api.NPULowerCase
devicePath = "/dev/"
davinciName = "davinci"
virtualDavinciName = "vdavinci"
davinciManager = "davinci_manager"
davinciManagerDocker = "davinci_manager_docker"
devmmSvm = "devmm_svm"
hisiHdc = "hisi_hdc"
// ... 310B管理设备列表
svm0, tsAisle, upgrade, sys, vdec, vpc, pngd, venc, logDrv, acodec, ai, ao, vo, hdmi
uburma, ummu
)
设备文件说明:
| 设备文件 | 用途 |
|---|---|
/dev/davinciN |
NPU计算设备,N是设备ID |
/dev/vdavinciN |
虚拟NPU设备(vNPU切分后) |
/dev/davinci_manager |
NPU管理设备 |
/dev/davinci_manager_docker |
Docker专用管理设备(容器内重映射) |
/dev/devmm_svm |
设备内存管理(SVM=Shared Virtual Memory) |
/dev/hisi_hdc |
华为HDC(Host Device Channel)通信通道 |
/dev/uburma |
UB(Ultra-Bandwidth)内存访问 |
/dev/ummu |
UB内存管理单元 |
三、核心类型定义
3.1 args结构体
go
type args struct {
bundleDirPath string
cmd string
}
bundleDirPath:OCI bundle目录路径,包含config.jsoncmd:命令类型,只有"create"需要处理,其他直接透传给runc
#mermaid-svg-NulvFiirrazZ6ARU{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-NulvFiirrazZ6ARU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NulvFiirrazZ6ARU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NulvFiirrazZ6ARU .error-icon{fill:#552222;}#mermaid-svg-NulvFiirrazZ6ARU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NulvFiirrazZ6ARU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NulvFiirrazZ6ARU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NulvFiirrazZ6ARU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NulvFiirrazZ6ARU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NulvFiirrazZ6ARU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NulvFiirrazZ6ARU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NulvFiirrazZ6ARU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NulvFiirrazZ6ARU .marker.cross{stroke:#333333;}#mermaid-svg-NulvFiirrazZ6ARU svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NulvFiirrazZ6ARU p{margin:0;}#mermaid-svg-NulvFiirrazZ6ARU g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-NulvFiirrazZ6ARU g.classGroup text .title{font-weight:bolder;}#mermaid-svg-NulvFiirrazZ6ARU .cluster-label text{fill:#333;}#mermaid-svg-NulvFiirrazZ6ARU .cluster-label span{color:#333;}#mermaid-svg-NulvFiirrazZ6ARU .cluster-label span p{background-color:transparent;}#mermaid-svg-NulvFiirrazZ6ARU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NulvFiirrazZ6ARU .cluster text{fill:#333;}#mermaid-svg-NulvFiirrazZ6ARU .cluster span{color:#333;}#mermaid-svg-NulvFiirrazZ6ARU .nodeLabel,#mermaid-svg-NulvFiirrazZ6ARU .edgeLabel{color:#131300;}#mermaid-svg-NulvFiirrazZ6ARU .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-NulvFiirrazZ6ARU .label text{fill:#131300;}#mermaid-svg-NulvFiirrazZ6ARU .labelBkg{background:#ECECFF;}#mermaid-svg-NulvFiirrazZ6ARU .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-NulvFiirrazZ6ARU .classTitle{font-weight:bolder;}#mermaid-svg-NulvFiirrazZ6ARU .node rect,#mermaid-svg-NulvFiirrazZ6ARU .node circle,#mermaid-svg-NulvFiirrazZ6ARU .node ellipse,#mermaid-svg-NulvFiirrazZ6ARU .node polygon,#mermaid-svg-NulvFiirrazZ6ARU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NulvFiirrazZ6ARU .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU g.clickable{cursor:pointer;}#mermaid-svg-NulvFiirrazZ6ARU g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-NulvFiirrazZ6ARU g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-NulvFiirrazZ6ARU .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-NulvFiirrazZ6ARU .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-NulvFiirrazZ6ARU .dashed-line{stroke-dasharray:3;}#mermaid-svg-NulvFiirrazZ6ARU .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-NulvFiirrazZ6ARU #compositionStart,#mermaid-svg-NulvFiirrazZ6ARU .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU #compositionEnd,#mermaid-svg-NulvFiirrazZ6ARU .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU #dependencyStart,#mermaid-svg-NulvFiirrazZ6ARU .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU #dependencyStart,#mermaid-svg-NulvFiirrazZ6ARU .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU #extensionStart,#mermaid-svg-NulvFiirrazZ6ARU .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU #extensionEnd,#mermaid-svg-NulvFiirrazZ6ARU .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU #aggregationStart,#mermaid-svg-NulvFiirrazZ6ARU .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU #aggregationEnd,#mermaid-svg-NulvFiirrazZ6ARU .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU #lollipopStart,#mermaid-svg-NulvFiirrazZ6ARU .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU #lollipopEnd,#mermaid-svg-NulvFiirrazZ6ARU .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-NulvFiirrazZ6ARU .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-NulvFiirrazZ6ARU .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NulvFiirrazZ6ARU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NulvFiirrazZ6ARU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NulvFiirrazZ6ARU :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} args
+string bundleDirPath
+string cmd
四、核心函数逐行解析
4.1 GetDeviceTypeByChipName(第113-127行)
go
func GetDeviceTypeByChipName(chipName string) string {
if strings.Contains(chipName, api.Ascend310BNo) {
return Ascend310B
}
if strings.Contains(chipName, api.Ascend310PNo) {
return Ascend310P
}
if strings.HasPrefix(chipName, api.Ascend910A5Prefix) {
return Ascend910A5
}
if strings.Contains(chipName, api.Ascend310No) {
return Ascend310
}
if strings.Contains(chipName, api.Ascend910No) {
return Ascend910
}
return ""
}
逐行解析:
- 如果芯片名包含"310B"字符串 → 返回
Ascend310B - 如果芯片名包含"310P" → 返回
Ascend310P - 注意 :A5用
HasPrefix而非Contains,因为A5芯片名有特定前缀模式Ascend950XX - 如果包含"310" → 返回
Ascend310 - 如果包含"910" → 返回
Ascend910 - 都不匹配 → 返回空字符串
匹配优先级设计意图:310B和310P必须在310之前检查,因为"310B"也包含"310"子串。这是字符串匹配的经典陷阱。
#mermaid-svg-eAJ3AWmvtVvVHbjp{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-eAJ3AWmvtVvVHbjp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-eAJ3AWmvtVvVHbjp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-eAJ3AWmvtVvVHbjp .error-icon{fill:#552222;}#mermaid-svg-eAJ3AWmvtVvVHbjp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eAJ3AWmvtVvVHbjp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-eAJ3AWmvtVvVHbjp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eAJ3AWmvtVvVHbjp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eAJ3AWmvtVvVHbjp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-eAJ3AWmvtVvVHbjp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eAJ3AWmvtVvVHbjp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eAJ3AWmvtVvVHbjp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eAJ3AWmvtVvVHbjp .marker.cross{stroke:#333333;}#mermaid-svg-eAJ3AWmvtVvVHbjp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eAJ3AWmvtVvVHbjp p{margin:0;}#mermaid-svg-eAJ3AWmvtVvVHbjp .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eAJ3AWmvtVvVHbjp .cluster-label text{fill:#333;}#mermaid-svg-eAJ3AWmvtVvVHbjp .cluster-label span{color:#333;}#mermaid-svg-eAJ3AWmvtVvVHbjp .cluster-label span p{background-color:transparent;}#mermaid-svg-eAJ3AWmvtVvVHbjp .label text,#mermaid-svg-eAJ3AWmvtVvVHbjp span{fill:#333;color:#333;}#mermaid-svg-eAJ3AWmvtVvVHbjp .node rect,#mermaid-svg-eAJ3AWmvtVvVHbjp .node circle,#mermaid-svg-eAJ3AWmvtVvVHbjp .node ellipse,#mermaid-svg-eAJ3AWmvtVvVHbjp .node polygon,#mermaid-svg-eAJ3AWmvtVvVHbjp .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eAJ3AWmvtVvVHbjp .rough-node .label text,#mermaid-svg-eAJ3AWmvtVvVHbjp .node .label text,#mermaid-svg-eAJ3AWmvtVvVHbjp .image-shape .label,#mermaid-svg-eAJ3AWmvtVvVHbjp .icon-shape .label{text-anchor:middle;}#mermaid-svg-eAJ3AWmvtVvVHbjp .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-eAJ3AWmvtVvVHbjp .rough-node .label,#mermaid-svg-eAJ3AWmvtVvVHbjp .node .label,#mermaid-svg-eAJ3AWmvtVvVHbjp .image-shape .label,#mermaid-svg-eAJ3AWmvtVvVHbjp .icon-shape .label{text-align:center;}#mermaid-svg-eAJ3AWmvtVvVHbjp .node.clickable{cursor:pointer;}#mermaid-svg-eAJ3AWmvtVvVHbjp .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-eAJ3AWmvtVvVHbjp .arrowheadPath{fill:#333333;}#mermaid-svg-eAJ3AWmvtVvVHbjp .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eAJ3AWmvtVvVHbjp .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eAJ3AWmvtVvVHbjp .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eAJ3AWmvtVvVHbjp .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-eAJ3AWmvtVvVHbjp .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eAJ3AWmvtVvVHbjp .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-eAJ3AWmvtVvVHbjp .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eAJ3AWmvtVvVHbjp .cluster text{fill:#333;}#mermaid-svg-eAJ3AWmvtVvVHbjp .cluster span{color:#333;}#mermaid-svg-eAJ3AWmvtVvVHbjp 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-eAJ3AWmvtVvVHbjp .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-eAJ3AWmvtVvVHbjp rect.text{fill:none;stroke-width:0;}#mermaid-svg-eAJ3AWmvtVvVHbjp .icon-shape,#mermaid-svg-eAJ3AWmvtVvVHbjp .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-eAJ3AWmvtVvVHbjp .icon-shape p,#mermaid-svg-eAJ3AWmvtVvVHbjp .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-eAJ3AWmvtVvVHbjp .icon-shape .label rect,#mermaid-svg-eAJ3AWmvtVvVHbjp .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-eAJ3AWmvtVvVHbjp .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-eAJ3AWmvtVvVHbjp .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-eAJ3AWmvtVvVHbjp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
是
否
是
否
是
否
chipName输入
包含310B?
返回 Ascend310B
包含310P?
返回 Ascend310P
前缀是Ascend950?
返回 Ascend910A5
包含310?
返回 Ascend310
包含910?
返回 Ascend910
返回空字符串
4.2 getArgs(第129-144行)
go
func getArgs() (*args, error) {
args := &args{}
for i, param := range os.Args {
if param == "--bundle" || param == "-b" {
if len(os.Args)-i <= 1 {
return nil, fmt.Errorf("bundle option needs an argument")
}
args.bundleDirPath = os.Args[i+1]
} else if param == "create" {
args.cmd = param
}
}
return args, nil
}
逐行解析:
- 创建空
args结构体 - 遍历
os.Args(命令行参数数组) - 遇到
--bundle或-b:检查下一个参数是否存在,存在则设为bundleDirPath - 遇到
create:设为cmd - 返回解析结果
设计意图 :OCI Runtime调用格式为 ascend-docker-runtime create --bundle /path,需要从中提取bundle路径和命令类型。
4.3 InitLogModule(第147-160行)
go
func InitLogModule(ctx context.Context) error {
const backups = 2
const logMaxAge = 365
const fileMaxSize = 2
runLogConfig := hwlog.LogConfig{
LogFileName: runLogPath,
LogLevel: 0, // DEBUG级别
MaxBackups: backups, // 最多2个备份
MaxAge: logMaxAge, // 保留365天
OnlyToFile: true, // 仅写文件不输出stdout
FileMaxSize: fileMaxSize, // 单文件2MB
}
if err := hwlog.InitRunLogger(&runLogConfig, ctx); err != nil {
fmt.Printf("hwlog init failed, error is %v", err)
return err
}
return nil
}
日志配置详解:
| 配置项 | 值 | 含义 |
|---|---|---|
| LogFileName | api.RunTimeRunLogPath | 日志文件路径 |
| LogLevel | 0 | DEBUG级别(最详细) |
| MaxBackups | 2 | 最多保留2个轮转备份 |
| MaxAge | 365 | 日志最大保留天数 |
| OnlyToFile | true | 仅写文件,不输出到stdout |
| FileMaxSize | 2 | 单个日志文件最大2MB |
设计意图 :OnlyToFile=true 防止日志干扰stdout,因为OCI Runtime的stdout会被runc捕获。
4.4 execRunc(第162-181行)
go
var execRunc = func() error {
tempRuncPath, err := exec.LookPath(dockerRuncName)
if err != nil {
tempRuncPath, err = exec.LookPath(runcName)
if err != nil {
return fmt.Errorf("failed to find the path of runc: %v", err)
}
}
runcPath, err := filepath.EvalSymlinks(tempRuncPath)
if err != nil {
return fmt.Errorf("failed to find realpath of runc %v", err)
}
if _, err := mindxcheckutils.RealFileChecker(runcPath, true, false, mindxcheckutils.DefaultSize); err != nil {
return err
}
if err := mindxcheckutils.ChangeRuntimeLogMode("runtime-run-"); err != nil {
return err
}
if err = syscall.Exec(runcPath, append([]string{runcPath}, os.Args[1:]...), os.Environ()); err != nil {
return fmt.Errorf("failed to exec runc: %v", err)
}
return nil
}
逐行解析:
exec.LookPath("docker-runc")--- 在PATH中查找docker版runc- 如果找不到,降级查找
runc filepath.EvalSymlinks--- 解析符号链接获取真实路径RealFileChecker--- 安全校验runc文件(防篡改)ChangeRuntimeLogMode--- 收紧日志文件权限syscall.Exec--- 替换当前进程为runc(不是fork/exec,是execve)
关键设计 :syscall.Exec 会将当前进程的内存镜像替换为runc,PID不变。这意味着ascend-docker-runtime进程"变成"了runc进程。传递的参数是 os.Args[1:](去掉第一个参数,即程序名本身),环境变量原样传递。
#mermaid-svg-BGtpV7R9FC8Yf102{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-BGtpV7R9FC8Yf102 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BGtpV7R9FC8Yf102 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BGtpV7R9FC8Yf102 .error-icon{fill:#552222;}#mermaid-svg-BGtpV7R9FC8Yf102 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BGtpV7R9FC8Yf102 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BGtpV7R9FC8Yf102 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BGtpV7R9FC8Yf102 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BGtpV7R9FC8Yf102 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BGtpV7R9FC8Yf102 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BGtpV7R9FC8Yf102 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BGtpV7R9FC8Yf102 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BGtpV7R9FC8Yf102 .marker.cross{stroke:#333333;}#mermaid-svg-BGtpV7R9FC8Yf102 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BGtpV7R9FC8Yf102 p{margin:0;}#mermaid-svg-BGtpV7R9FC8Yf102 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BGtpV7R9FC8Yf102 .cluster-label text{fill:#333;}#mermaid-svg-BGtpV7R9FC8Yf102 .cluster-label span{color:#333;}#mermaid-svg-BGtpV7R9FC8Yf102 .cluster-label span p{background-color:transparent;}#mermaid-svg-BGtpV7R9FC8Yf102 .label text,#mermaid-svg-BGtpV7R9FC8Yf102 span{fill:#333;color:#333;}#mermaid-svg-BGtpV7R9FC8Yf102 .node rect,#mermaid-svg-BGtpV7R9FC8Yf102 .node circle,#mermaid-svg-BGtpV7R9FC8Yf102 .node ellipse,#mermaid-svg-BGtpV7R9FC8Yf102 .node polygon,#mermaid-svg-BGtpV7R9FC8Yf102 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BGtpV7R9FC8Yf102 .rough-node .label text,#mermaid-svg-BGtpV7R9FC8Yf102 .node .label text,#mermaid-svg-BGtpV7R9FC8Yf102 .image-shape .label,#mermaid-svg-BGtpV7R9FC8Yf102 .icon-shape .label{text-anchor:middle;}#mermaid-svg-BGtpV7R9FC8Yf102 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BGtpV7R9FC8Yf102 .rough-node .label,#mermaid-svg-BGtpV7R9FC8Yf102 .node .label,#mermaid-svg-BGtpV7R9FC8Yf102 .image-shape .label,#mermaid-svg-BGtpV7R9FC8Yf102 .icon-shape .label{text-align:center;}#mermaid-svg-BGtpV7R9FC8Yf102 .node.clickable{cursor:pointer;}#mermaid-svg-BGtpV7R9FC8Yf102 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BGtpV7R9FC8Yf102 .arrowheadPath{fill:#333333;}#mermaid-svg-BGtpV7R9FC8Yf102 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BGtpV7R9FC8Yf102 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BGtpV7R9FC8Yf102 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BGtpV7R9FC8Yf102 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BGtpV7R9FC8Yf102 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BGtpV7R9FC8Yf102 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BGtpV7R9FC8Yf102 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BGtpV7R9FC8Yf102 .cluster text{fill:#333;}#mermaid-svg-BGtpV7R9FC8Yf102 .cluster span{color:#333;}#mermaid-svg-BGtpV7R9FC8Yf102 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-BGtpV7R9FC8Yf102 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BGtpV7R9FC8Yf102 rect.text{fill:none;stroke-width:0;}#mermaid-svg-BGtpV7R9FC8Yf102 .icon-shape,#mermaid-svg-BGtpV7R9FC8Yf102 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BGtpV7R9FC8Yf102 .icon-shape p,#mermaid-svg-BGtpV7R9FC8Yf102 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BGtpV7R9FC8Yf102 .icon-shape .label rect,#mermaid-svg-BGtpV7R9FC8Yf102 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BGtpV7R9FC8Yf102 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BGtpV7R9FC8Yf102 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BGtpV7R9FC8Yf102 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 找到
未找到
找到
未找到
不通过
通过
成功
失败
execRunc启动
LookPath docker-runc
EvalSymlinks解析符号链接
LookPath runc
返回错误
RealFileChecker安全校验
返回错误
ChangeRuntimeLogMode收紧日志权限
syscall.Exec替换进程为runc
进程变为runc,不会返回
返回错误
4.5 addAscendDockerEnv(第183-188行)
go
func addAscendDockerEnv(spec *specs.Spec) {
if spec == nil || spec.Process == nil || spec.Process.Env == nil {
return
}
spec.Process.Env = append(spec.Process.Env, useAscendDocker)
}
- 向容器的环境变量列表追加
ASCEND_DOCKER_RUNTIME=True - 三重nil检查防止空指针panic
- 设计意图:让容器内的应用能检测到自己运行在ascend-docker-runtime环境中
4.6 addHook(第190-228行)
go
func addHook(w dcmi.WorkerInterface, spec *specs.Spec, deviceIdList *[]int) error {
这是最复杂的函数之一,负责注入Prestart Hook和创建虚拟设备。
go
if deviceIdList == nil {
return nil
}
currentExecPath, err := os.Executable()
if err != nil {
return fmt.Errorf("cannot get the path of docker-runtime: %v", err)
}
- nil检查保护
os.Executable()获取当前二进制文件路径(如/usr/local/bin/ascend-docker-runtime)
go
hookCliPath = path.Join(path.Dir(currentExecPath), hookCli)
if _, err := mindxcheckutils.RealFileChecker(hookCliPath, true, false, mindxcheckutils.DefaultSize); err != nil {
return err
}
if _, err = os.Stat(hookCliPath); err != nil {
return fmt.Errorf("cannot find docker-hook executable file at %s: %v", hookCliPath, err)
}
- 构造hook路径:与runtime同目录下的
ascend-docker-hook - 双重校验:
RealFileChecker(安全校验)+os.Stat(存在性检查)
go
if spec.Hooks == nil {
spec.Hooks = &specs.Hooks{}
}
needUpdate := true
if len(spec.Hooks.Prestart) > MaxCommandLength {
return fmt.Errorf("too many items in Prestart ")
}
for _, hook := range spec.Hooks.Prestart {
if strings.Contains(hook.Path, hookCli) {
needUpdate = false
break
}
}
if needUpdate {
spec.Hooks.Prestart = append(spec.Hooks.Prestart, specs.Hook{
Path: hookCliPath,
Args: []string{hookCliPath},
})
}
- 初始化Hooks结构(如果不存在)
- 安全限制:Prestart数量不超过65535
- 幂等性检查:遍历已有Prestart Hook,如果已包含ascend-docker-hook则不重复添加
- 添加Hook:Path为hook二进制路径,Args仅包含路径本身
go
if len(spec.Process.Env) > MaxCommandLength {
return fmt.Errorf("too many items in Env ")
}
if strings.Contains(getValueByKey(spec.Process.Env, ascendRuntimeOptions), "VIRTUAL") {
return nil
}
vdevice, err := dcmi.CreateVDevice(w, spec, *deviceIdList)
if err != nil {
return err
}
hwlog.RunLog.Infof("vnpu split done: vdevice: %v", vdevice.VdeviceID)
if vdevice.VdeviceID != -1 {
if err = updateEnvAndPostHook(spec, vdevice, deviceIdList); err != nil {
return fmt.Errorf("update evn and post hook failed: %v ", err)
}
}
return nil
}
- 如果环境变量包含"VIRTUAL" → 已是虚拟模式,跳过vNPU创建
- 调用
dcmi.CreateVDevice创建虚拟设备 - 如果创建成功(VdeviceID != -1),更新环境变量并注入Poststop Hook
#mermaid-svg-aW1JFazCq87XcUkK{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-aW1JFazCq87XcUkK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-aW1JFazCq87XcUkK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-aW1JFazCq87XcUkK .error-icon{fill:#552222;}#mermaid-svg-aW1JFazCq87XcUkK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-aW1JFazCq87XcUkK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-aW1JFazCq87XcUkK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-aW1JFazCq87XcUkK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-aW1JFazCq87XcUkK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-aW1JFazCq87XcUkK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-aW1JFazCq87XcUkK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-aW1JFazCq87XcUkK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-aW1JFazCq87XcUkK .marker.cross{stroke:#333333;}#mermaid-svg-aW1JFazCq87XcUkK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-aW1JFazCq87XcUkK p{margin:0;}#mermaid-svg-aW1JFazCq87XcUkK .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-aW1JFazCq87XcUkK .cluster-label text{fill:#333;}#mermaid-svg-aW1JFazCq87XcUkK .cluster-label span{color:#333;}#mermaid-svg-aW1JFazCq87XcUkK .cluster-label span p{background-color:transparent;}#mermaid-svg-aW1JFazCq87XcUkK .label text,#mermaid-svg-aW1JFazCq87XcUkK span{fill:#333;color:#333;}#mermaid-svg-aW1JFazCq87XcUkK .node rect,#mermaid-svg-aW1JFazCq87XcUkK .node circle,#mermaid-svg-aW1JFazCq87XcUkK .node ellipse,#mermaid-svg-aW1JFazCq87XcUkK .node polygon,#mermaid-svg-aW1JFazCq87XcUkK .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-aW1JFazCq87XcUkK .rough-node .label text,#mermaid-svg-aW1JFazCq87XcUkK .node .label text,#mermaid-svg-aW1JFazCq87XcUkK .image-shape .label,#mermaid-svg-aW1JFazCq87XcUkK .icon-shape .label{text-anchor:middle;}#mermaid-svg-aW1JFazCq87XcUkK .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-aW1JFazCq87XcUkK .rough-node .label,#mermaid-svg-aW1JFazCq87XcUkK .node .label,#mermaid-svg-aW1JFazCq87XcUkK .image-shape .label,#mermaid-svg-aW1JFazCq87XcUkK .icon-shape .label{text-align:center;}#mermaid-svg-aW1JFazCq87XcUkK .node.clickable{cursor:pointer;}#mermaid-svg-aW1JFazCq87XcUkK .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-aW1JFazCq87XcUkK .arrowheadPath{fill:#333333;}#mermaid-svg-aW1JFazCq87XcUkK .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-aW1JFazCq87XcUkK .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-aW1JFazCq87XcUkK .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aW1JFazCq87XcUkK .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-aW1JFazCq87XcUkK .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aW1JFazCq87XcUkK .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-aW1JFazCq87XcUkK .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-aW1JFazCq87XcUkK .cluster text{fill:#333;}#mermaid-svg-aW1JFazCq87XcUkK .cluster span{color:#333;}#mermaid-svg-aW1JFazCq87XcUkK 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-aW1JFazCq87XcUkK .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-aW1JFazCq87XcUkK rect.text{fill:none;stroke-width:0;}#mermaid-svg-aW1JFazCq87XcUkK .icon-shape,#mermaid-svg-aW1JFazCq87XcUkK .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-aW1JFazCq87XcUkK .icon-shape p,#mermaid-svg-aW1JFazCq87XcUkK .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-aW1JFazCq87XcUkK .icon-shape .label rect,#mermaid-svg-aW1JFazCq87XcUkK .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-aW1JFazCq87XcUkK .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-aW1JFazCq87XcUkK .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-aW1JFazCq87XcUkK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
是
否
成功
失败
addHook入口
nil检查
获取当前二进制路径
构造hook路径
同目录下的ascend-docker-hook
安全校验hook文件
初始化Spec.Hooks
Prestart已包含
ascend-docker-hook?
跳过添加
追加Prestart Hook
环境变量含VIRTUAL?
跳过vNPU创建
直接返回
CreateVDevice
创建虚拟设备
updateEnvAndPostHook
更新Env+注入Poststop
返回错误
返回nil
4.7 parseDevices / getDeviceListFromVisibleValue(第248-291行)
go
func parseDevices(visibleDevices string) ([]int, error) {
devices := make([]int, 0)
for _, value := range strings.Split(visibleDevices, ",") {
deviceFromValue, err := getDeviceListFromVisibleValue(value)
if err != nil {
return nil, err
}
devices = append(devices, deviceFromValue...)
}
sort.Slice(devices, func(i, j int) bool { return i < j })
return removeDuplication(devices), nil
}
- 按逗号分割设备列表(如 "0,1,2" → "0","1","2")
- 对每个值调用
getDeviceListFromVisibleValue解析(支持范围"0-3") - 排序后去重
go
func getDeviceListFromVisibleValue(visibleValue string) ([]int, error) {
maxDevice := 128
devices := make([]int, 0)
visibleValue = strings.TrimSpace(visibleValue)
if strings.Contains(visibleValue, "-") {
// 范围解析: "0-3" → [0,1,2,3]
borders := strings.Split(visibleValue, "-")
if len(borders) != borderNum {
return nil, fmt.Errorf("invalid device range: %s", visibleValue)
}
left, err := strconv.Atoi(borders[0])
if err != nil || left < 0 {
return nil, fmt.Errorf("invalid left boarder range parameter: %s", borders[0])
}
right, err := strconv.Atoi(borders[1])
if err != nil || right > maxDevice {
return nil, fmt.Errorf("invalid right boarder range parameter: %s", borders[1])
}
if left > right {
return nil, fmt.Errorf("left boarder (%d) should not be larger than the right one(%d)", left, right)
}
for n := left; n <= right; n++ {
devices = append(devices, n)
}
return devices, nil
}
// 单个设备: "5" → [5]
n, err := strconv.Atoi(visibleValue)
if err != nil {
return nil, fmt.Errorf("invalid single device parameter: %s", visibleValue)
}
devices = append(devices, n)
return devices, nil
}
支持的格式:
| 输入格式 | 输出 | 说明 |
|---|---|---|
"0" |
[0] |
单个设备 |
"0,1,2" |
[0,1,2] |
多个设备 |
"0-3" |
[0,1,2,3] |
范围设备 |
"0-3,7,9-11" |
[0,1,2,3,7,9,10,11] |
混合格式 |
边界校验:左边界≥0,右边界≤128,左≤右。
4.8 parseAscendDevices(第293-311行)
go
func parseAscendDevices(visibleDevices string) ([]int, error) {
devicesList := strings.Split(visibleDevices, ",")
devices := make([]int, 0, len(devicesList))
for _, d := range devicesList {
matchGroups := regexp.MustCompile(deviceRegx).FindStringSubmatch(strings.TrimSpace(d))
if matchGroups == nil {
return nil, fmt.Errorf("invalid device format: %s", d)
}
n, err := strconv.Atoi(matchGroups[2])
if err != nil {
return nil, fmt.Errorf("invalid device id: %s", d)
}
devices = append(devices, n)
}
sort.Slice(devices, func(i, j int) bool { return i < j })
return removeDuplication(devices), nil
}
- 使用正则
^(?:Ascend(910|310B|310P|310)|npu)-(\d+)$匹配 matchGroups[2]是第二个捕获组(设备ID数字)- 排序+去重
与parseDevices的区别 :parseDevices 处理纯数字格式("0,1,2"),parseAscendDevices 处理命名格式("Ascend910-0,npu-1")。
4.9 getValueByKey / getValueByDeviceKey(第313-345行)
go
func getValueByKey(data []string, name string) string {
for _, envLine := range data {
words := strings.SplitN(envLine, "=", kvPairSize)
if len(words) != kvPairSize {
hwlog.RunLog.Error("environment error")
return ""
}
if words[0] == name {
return words[1]
}
}
return ""
}
- 从
["KEY1=val1", "KEY2=val2"]格式的数组中查找指定key的值 - SplitN限制分割数为2,防止值中包含"="时出错
go
func getValueByDeviceKey(data []string) string {
res := ""
for i := len(data) - 1; i >= 0; i-- {
words := strings.SplitN(data[i], "=", kvPairSize)
if len(words) != kvPairSize {
hwlog.RunLog.Error("environment error")
return ""
}
if words[0] == ascendVisibleDevices {
res = words[1]
break
}
}
// ...
return res
}
关键差异 :getValueByDeviceKey 从后往前遍历 。设计意图:当环境变量中存在多个 ASCEND_VISIBLE_DEVICES 时,取最后一个生效的值(后写覆盖前写,模拟Docker环境变量的覆盖语义)。
4.10 addDeviceToSpec(第360-375行)
go
func addDeviceToSpec(spec *specs.Spec, dHostPath string, dContainerPath string) error {
device, err := oci.DeviceFromPath(dHostPath)
if err != nil {
return fmt.Errorf("failed to get %s info : %v", dHostPath, err)
}
device.Path = dContainerPath
spec.Linux.Devices = append(spec.Linux.Devices, *device)
newDeviceCgroup := specs.LinuxDeviceCgroup{
Allow: true,
Type: device.Type,
Major: &device.Major,
Minor: &device.Minor,
Access: "rwm",
}
spec.Linux.Resources.Devices = append(spec.Linux.Resources.Devices, newDeviceCgroup)
return nil
}
逐行解析:
oci.DeviceFromPath(dHostPath)--- 获取Host路径对应的设备信息(类型/主设备号/次设备号)device.Path = dContainerPath--- 设置容器内路径(可能与Host路径不同,如vdavinci重映射)- 追加到
Spec.Linux.Devices--- runc会根据此列表在容器namespace中创建设备节点 - 创建cgroup规则 --- 允许容器访问该设备的 主:次 设备号,权限"rwm"(读写+mknod)
设计意图:OCI Spec的设备注入需要两步:1)声明设备(Linux.Devices),2)允许访问(Linux.Resources.Devices cgroup规则)。缺少任何一步都无法正常访问设备。
#mermaid-svg-9FDDS3EQ5xukK99o{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-9FDDS3EQ5xukK99o .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9FDDS3EQ5xukK99o .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9FDDS3EQ5xukK99o .error-icon{fill:#552222;}#mermaid-svg-9FDDS3EQ5xukK99o .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9FDDS3EQ5xukK99o .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9FDDS3EQ5xukK99o .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9FDDS3EQ5xukK99o .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9FDDS3EQ5xukK99o .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9FDDS3EQ5xukK99o .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9FDDS3EQ5xukK99o .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9FDDS3EQ5xukK99o .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9FDDS3EQ5xukK99o .marker.cross{stroke:#333333;}#mermaid-svg-9FDDS3EQ5xukK99o svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9FDDS3EQ5xukK99o p{margin:0;}#mermaid-svg-9FDDS3EQ5xukK99o .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9FDDS3EQ5xukK99o .cluster-label text{fill:#333;}#mermaid-svg-9FDDS3EQ5xukK99o .cluster-label span{color:#333;}#mermaid-svg-9FDDS3EQ5xukK99o .cluster-label span p{background-color:transparent;}#mermaid-svg-9FDDS3EQ5xukK99o .label text,#mermaid-svg-9FDDS3EQ5xukK99o span{fill:#333;color:#333;}#mermaid-svg-9FDDS3EQ5xukK99o .node rect,#mermaid-svg-9FDDS3EQ5xukK99o .node circle,#mermaid-svg-9FDDS3EQ5xukK99o .node ellipse,#mermaid-svg-9FDDS3EQ5xukK99o .node polygon,#mermaid-svg-9FDDS3EQ5xukK99o .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9FDDS3EQ5xukK99o .rough-node .label text,#mermaid-svg-9FDDS3EQ5xukK99o .node .label text,#mermaid-svg-9FDDS3EQ5xukK99o .image-shape .label,#mermaid-svg-9FDDS3EQ5xukK99o .icon-shape .label{text-anchor:middle;}#mermaid-svg-9FDDS3EQ5xukK99o .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9FDDS3EQ5xukK99o .rough-node .label,#mermaid-svg-9FDDS3EQ5xukK99o .node .label,#mermaid-svg-9FDDS3EQ5xukK99o .image-shape .label,#mermaid-svg-9FDDS3EQ5xukK99o .icon-shape .label{text-align:center;}#mermaid-svg-9FDDS3EQ5xukK99o .node.clickable{cursor:pointer;}#mermaid-svg-9FDDS3EQ5xukK99o .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9FDDS3EQ5xukK99o .arrowheadPath{fill:#333333;}#mermaid-svg-9FDDS3EQ5xukK99o .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9FDDS3EQ5xukK99o .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9FDDS3EQ5xukK99o .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9FDDS3EQ5xukK99o .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9FDDS3EQ5xukK99o .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9FDDS3EQ5xukK99o .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9FDDS3EQ5xukK99o .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9FDDS3EQ5xukK99o .cluster text{fill:#333;}#mermaid-svg-9FDDS3EQ5xukK99o .cluster span{color:#333;}#mermaid-svg-9FDDS3EQ5xukK99o 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-9FDDS3EQ5xukK99o .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9FDDS3EQ5xukK99o rect.text{fill:none;stroke-width:0;}#mermaid-svg-9FDDS3EQ5xukK99o .icon-shape,#mermaid-svg-9FDDS3EQ5xukK99o .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9FDDS3EQ5xukK99o .icon-shape p,#mermaid-svg-9FDDS3EQ5xukK99o .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9FDDS3EQ5xukK99o .icon-shape .label rect,#mermaid-svg-9FDDS3EQ5xukK99o .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9FDDS3EQ5xukK99o .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9FDDS3EQ5xukK99o .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9FDDS3EQ5xukK99o :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} dHostPath
/dev/davinci0
oci.DeviceFromPath
获取设备类型+主次号
设置containerPath
/dev/davinci0或vdavinci0
追加到Spec.Linux.Devices
声明设备节点
追加到Spec.Linux.Resources.Devices
cgroup允许访问
runc读取Spec
容器namespace中创建设备
+cgroup授权
4.11 addManagerDevice(第408-455行)
go
func addManagerDevice(w dcmi.WorkerInterface, spec *specs.Spec) error {
chipName, err := w.GetChipName()
// ...
devType := GetDeviceTypeByChipName(chipName)
// ...
if devType != "" && devType != Ascend910A5 {
dPath := devicePath + dvppCmdList
if err := addDeviceToSpec(spec, dPath, dPath); err != nil {
hwlog.RunLog.Warnf("failed to add dvpp_cmdlist to spec : %v", err)
}
}
if devType == Ascend310B {
return addAscend310BManagerDevice(spec)
}
// 添加 davinci_manager
dPath := devicePath + davinciManager
if err := addDeviceToSpec(spec, dPath, dPath); err != nil {
return fmt.Errorf("add davinci_manager to spec error: %v", err)
}
productType, err := w.GetProductType()
// ...
switch productType {
case Atlas200ISoc, Atlas200:
// 这两种产品不需要额外管理设备
default:
if err = addCommonManagerDevice(spec, devType); err != nil {
return fmt.Errorf("add common manage device error: %v", err)
}
}
return nil
}
逐行解析:
- 获取芯片名称 → 确定设备类型
- 如果非空且非A5 → 添加
dvpp_cmdlist(数字视觉预处理命令列表设备) - 如果是310B → 调用专门的310B管理设备添加函数(14个设备)
- 添加
davinci_manager(所有芯片都需要) - 获取产品类型
- Atlas 200I SoC / Atlas 200 → 不添加额外管理设备
- 其他产品 → 添加通用管理设备(devmm_svm + hisi_hdc,A5仅hisi_hdc)
#mermaid-svg-BIKY5cNNsacDSwRf{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-BIKY5cNNsacDSwRf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BIKY5cNNsacDSwRf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BIKY5cNNsacDSwRf .error-icon{fill:#552222;}#mermaid-svg-BIKY5cNNsacDSwRf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BIKY5cNNsacDSwRf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BIKY5cNNsacDSwRf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BIKY5cNNsacDSwRf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BIKY5cNNsacDSwRf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BIKY5cNNsacDSwRf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BIKY5cNNsacDSwRf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BIKY5cNNsacDSwRf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BIKY5cNNsacDSwRf .marker.cross{stroke:#333333;}#mermaid-svg-BIKY5cNNsacDSwRf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BIKY5cNNsacDSwRf p{margin:0;}#mermaid-svg-BIKY5cNNsacDSwRf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-BIKY5cNNsacDSwRf .cluster-label text{fill:#333;}#mermaid-svg-BIKY5cNNsacDSwRf .cluster-label span{color:#333;}#mermaid-svg-BIKY5cNNsacDSwRf .cluster-label span p{background-color:transparent;}#mermaid-svg-BIKY5cNNsacDSwRf .label text,#mermaid-svg-BIKY5cNNsacDSwRf span{fill:#333;color:#333;}#mermaid-svg-BIKY5cNNsacDSwRf .node rect,#mermaid-svg-BIKY5cNNsacDSwRf .node circle,#mermaid-svg-BIKY5cNNsacDSwRf .node ellipse,#mermaid-svg-BIKY5cNNsacDSwRf .node polygon,#mermaid-svg-BIKY5cNNsacDSwRf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BIKY5cNNsacDSwRf .rough-node .label text,#mermaid-svg-BIKY5cNNsacDSwRf .node .label text,#mermaid-svg-BIKY5cNNsacDSwRf .image-shape .label,#mermaid-svg-BIKY5cNNsacDSwRf .icon-shape .label{text-anchor:middle;}#mermaid-svg-BIKY5cNNsacDSwRf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BIKY5cNNsacDSwRf .rough-node .label,#mermaid-svg-BIKY5cNNsacDSwRf .node .label,#mermaid-svg-BIKY5cNNsacDSwRf .image-shape .label,#mermaid-svg-BIKY5cNNsacDSwRf .icon-shape .label{text-align:center;}#mermaid-svg-BIKY5cNNsacDSwRf .node.clickable{cursor:pointer;}#mermaid-svg-BIKY5cNNsacDSwRf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BIKY5cNNsacDSwRf .arrowheadPath{fill:#333333;}#mermaid-svg-BIKY5cNNsacDSwRf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BIKY5cNNsacDSwRf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BIKY5cNNsacDSwRf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BIKY5cNNsacDSwRf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BIKY5cNNsacDSwRf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BIKY5cNNsacDSwRf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BIKY5cNNsacDSwRf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BIKY5cNNsacDSwRf .cluster text{fill:#333;}#mermaid-svg-BIKY5cNNsacDSwRf .cluster span{color:#333;}#mermaid-svg-BIKY5cNNsacDSwRf 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-BIKY5cNNsacDSwRf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BIKY5cNNsacDSwRf rect.text{fill:none;stroke-width:0;}#mermaid-svg-BIKY5cNNsacDSwRf .icon-shape,#mermaid-svg-BIKY5cNNsacDSwRf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BIKY5cNNsacDSwRf .icon-shape p,#mermaid-svg-BIKY5cNNsacDSwRf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BIKY5cNNsacDSwRf .icon-shape .label rect,#mermaid-svg-BIKY5cNNsacDSwRf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BIKY5cNNsacDSwRf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BIKY5cNNsacDSwRf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BIKY5cNNsacDSwRf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
是
否
addManagerDevice
GetChipName
获取芯片名
GetDeviceTypeByChipName
确定设备类型
类型为空或A5?
添加 dvpp_cmdlist
类型是310B?
addAscend310BManagerDevice
添加14个管理设备
添加 davinci_manager
GetProductType
获取产品类型
Atlas 200I SoC
或 Atlas 200?
不添加额外设备
addCommonManagerDevice
添加 devmm_svm + hisi_hdc
4.12 checkVisibleDevice(第500-522行)
go
func checkVisibleDevice(spec *specs.Spec) ([]int, error) {
if spec.Process == nil {
return nil, errors.New("empty process info")
}
visibleDevices := getValueByDeviceKey(spec.Process.Env)
if visibleDevices == "" || visibleDevices == void {
return nil, nil
}
if strings.Contains(visibleDevices, ascend) || strings.Contains(visibleDevices, npu) {
devices, err := parseAscendDevices(visibleDevices)
// ...
return devices, err
}
devices, err := parseDevices(visibleDevices)
// ...
return devices, err
}
逐行解析:
- nil检查
spec.Process - 从环境变量从后往前 查找
ASCEND_VISIBLE_DEVICES - 空或"void" → 返回空列表(不挂载NPU)
- 如果包含"Ascend"或"npu" → 使用命名格式解析(
parseAscendDevices) - 否则 → 使用纯数字格式解析(
parseDevices)
4.13 addDevice(第524-545行)
go
func addDevice(w dcmi.WorkerInterface, spec *specs.Spec, deviceIdList []int) error {
deviceName := davinciName
if strings.Contains(getValueByKey(spec.Process.Env, ascendRuntimeOptions), "VIRTUAL") {
deviceName = virtualDavinciName
}
for _, deviceId := range deviceIdList {
dPath := devicePath + deviceName + strconv.Itoa(deviceId)
dContainerPath, err := getMountPath(dPath, deviceName)
// ...
if err := addDeviceToSpec(spec, dPath, dContainerPath); err != nil {
return fmt.Errorf("failed to add davinci device to spec: %v", err)
}
}
if err := addUBDevice(spec); err != nil {
// ...
}
if err := addManagerDevice(w, spec); err != nil {
// ...
}
return nil
}
逐行解析:
- 默认设备名为"davinci",如果VIRTUAL模式则改为"vdavinci"
- 遍历设备ID列表,对每个设备:
- 构造Host路径:
/dev/davinci0或/dev/vdavinci0 getMountPath获取容器内路径(vdavinci会重映射为davinci+N)- 添加到Spec
- 构造Host路径:
- 添加UB设备(uburma/ummu目录下的设备)
- 添加管理设备
4.14 updateEnvAndPostHook(第547-581行)
go
func updateEnvAndPostHook(spec *specs.Spec, vdevice dcmi.VDeviceInfo, deviceIdList *[]int) error {
此函数在vNPU创建成功后调用,负责更新环境变量和注入Poststop Hook。
go
newEnv := make([]string, 0, len(spec.Process.Env)+1)
needAddVirtualFlag := true
*deviceIdList = []int{int(vdevice.VdeviceID)}
- 关键操作 :将设备ID列表替换为虚拟设备ID(
vdevice.VdeviceID) - 后续
addDevice会使用这个新的ID来挂载/dev/vdavinci{VdeviceID}
go
for _, line := range spec.Process.Env {
words := strings.Split(line, "=")
if len(words) == envLength && strings.TrimSpace(words[0]) == ascendRuntimeOptions {
needAddVirtualFlag = false
if strings.Contains(words[1], "VIRTUAL") {
newEnv = append(newEnv, line)
continue
} else {
newEnv = append(newEnv, strings.TrimSpace(line)+",VIRTUAL")
continue
}
}
newEnv = append(newEnv, line)
}
if needAddVirtualFlag {
newEnv = append(newEnv, fmt.Sprintf(api.AscendRuntimeOptionsEnv+"=VIRTUAL"))
}
spec.Process.Env = newEnv
- 遍历环境变量,找到
ASCEND_RUNTIME_OPTIONS - 如果已有VIRTUAL → 保持不变
- 如果没有VIRTUAL → 追加",VIRTUAL"
- 如果完全不存在 → 添加
ASCEND_RUNTIME_OPTIONS=VIRTUAL - 设计意图:确保Hook进程知道这是虚拟设备模式
go
currentExecPath, err := os.Executable()
// ...
postHookCliPath := path.Join(path.Dir(currentExecPath), destroyHookCli)
// ...安全校验...
spec.Hooks.Poststop = append(spec.Hooks.Poststop, specs.Hook{
Path: postHookCliPath,
Args: []string{postHookCliPath, fmt.Sprintf("%d", vdevice.CardID), fmt.Sprintf("%d", vdevice.DeviceID),
fmt.Sprintf("%d", vdevice.VdeviceID)},
})
return nil
}
- 构造destroy程序路径(与runtime同目录下的
ascend-docker-destroy) - 安全校验
- 注入Poststop Hook,参数为 cardID、deviceID、vdeviceID
- 设计意图:容器停止时自动销毁虚拟设备,防止资源泄漏
4.15 modifySpecFile / readSpecFile / writeSpecFile(第583-640行)
go
func modifySpecFile(path string) error {
if err := mindxcheckutils.CheckPath(path, true); err != nil {
return err
}
spec, err := readSpecFile(path)
if err != nil {
return err
}
if err := processDevicesAndHooks(spec); err != nil {
return err
}
return writeSpecFile(path, spec)
}
三步操作:安全校验路径 → 读取并修改 → 写回。
go
func readSpecFile(path string) (*specs.Spec, error) {
stat, err := os.Stat(path)
// ...
jsonFile, err := os.OpenFile(path, os.O_RDWR, stat.Mode())
// ...
defer jsonFile.Close()
if err = mindxcheckutils.CheckFileInfo(jsonFile, mindxcheckutils.DefaultSize); err != nil {
return nil, err
}
jsonContent, err := ioutil.ReadAll(jsonFile)
// ...
var spec specs.Spec
if err = json.Unmarshal(jsonContent, &spec); err != nil {
return nil, err
}
return &spec, nil
}
CheckPath--- 路径安全校验(防../穿越、防软链接)CheckFileInfo--- 文件安全校验(大小限制、权限检查、UID检查、防setuid/setgid)json.Unmarshal--- JSON反序列化为specs.Spec结构体
go
func writeSpecFile(path string, spec *specs.Spec) error {
jsonFile, err := os.OpenFile(path, os.O_RDWR, 0)
// ...
jsonOutput, err := json.Marshal(spec)
// ...
if err = jsonFile.Truncate(0); err != nil {
return err
}
if _, err = jsonFile.WriteAt(jsonOutput, 0); err != nil {
return err
}
return nil
}
Truncate(0)--- 清空文件内容(因为修改后的JSON可能比原来短)WriteAt(jsonOutput, 0)--- 从文件开头写入- 设计意图:Truncate+WriteAt确保文件不会残留旧数据
4.16 processDevicesAndHooks(第642-658行)
go
func processDevicesAndHooks(spec *specs.Spec) error {
devices, err := checkVisibleDevice(spec)
if err != nil {
return fmt.Errorf("failed to check %v parameter, err: %v", api.AscendVisibleDevicesEnv, err)
}
if len(devices) != 0 {
npuWorker, err := dcmi.GetMatchingNpuWorker()
if err != nil {
return err
}
if err = addHook(npuWorker, spec, &devices); err != nil {
return fmt.Errorf("failed to inject hook, err: %v", err)
}
if err = addDevice(npuWorker, spec, devices); err != nil {
return fmt.Errorf("failed to add device to env: %v", err)
}
}
addAscendDockerEnv(spec)
return nil
}
核心编排函数,执行顺序:
checkVisibleDevice--- 检测是否需要NPU- 如果需要 →
GetMatchingNpuWorker选择驱动 addHook--- 注入Hook + 创建vNPUaddDevice--- 挂载设备addAscendDockerEnv--- 添加标识环境变量(无论是否需要NPU都添加)
4.17 DoProcess(第795-807行)
go
func DoProcess() error {
args, err := getArgs()
if err != nil {
return fmt.Errorf("failed to get args: %v", err)
}
if args.cmd != "create" {
return execRunc()
}
if args.bundleDirPath == "" {
hwlog.RunLog.Warn("get bundleDirPath is empty,try get current working dir from pwd ")
args.bundleDirPath, err = os.Getwd()
if err != nil {
return fmt.Errorf("failed to get current working dir: %v", err)
}
}
specFilePath := args.bundleDirPath + "/config.json"
if err = modifySpecFile(specFilePath); err != nil {
return fmt.Errorf("failed to modify spec file %s: %v", specFilePath, err)
}
return execRunc()
}
逐行解析:
getArgs()--- 解析命令行参数- 如果不是"create"命令 → 直接execRunc透传(如delete/kill/start等命令)
- 如果bundle路径为空 → 尝试用当前工作目录
- 构造config.json路径
modifySpecFile--- 读取→修改→写回execRunc--- 执行runc
#mermaid-svg-x0HJHKRLlYJ3ogEx{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-x0HJHKRLlYJ3ogEx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-x0HJHKRLlYJ3ogEx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-x0HJHKRLlYJ3ogEx .error-icon{fill:#552222;}#mermaid-svg-x0HJHKRLlYJ3ogEx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-x0HJHKRLlYJ3ogEx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-x0HJHKRLlYJ3ogEx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-x0HJHKRLlYJ3ogEx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-x0HJHKRLlYJ3ogEx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-x0HJHKRLlYJ3ogEx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-x0HJHKRLlYJ3ogEx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-x0HJHKRLlYJ3ogEx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-x0HJHKRLlYJ3ogEx .marker.cross{stroke:#333333;}#mermaid-svg-x0HJHKRLlYJ3ogEx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-x0HJHKRLlYJ3ogEx p{margin:0;}#mermaid-svg-x0HJHKRLlYJ3ogEx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-x0HJHKRLlYJ3ogEx .cluster-label text{fill:#333;}#mermaid-svg-x0HJHKRLlYJ3ogEx .cluster-label span{color:#333;}#mermaid-svg-x0HJHKRLlYJ3ogEx .cluster-label span p{background-color:transparent;}#mermaid-svg-x0HJHKRLlYJ3ogEx .label text,#mermaid-svg-x0HJHKRLlYJ3ogEx span{fill:#333;color:#333;}#mermaid-svg-x0HJHKRLlYJ3ogEx .node rect,#mermaid-svg-x0HJHKRLlYJ3ogEx .node circle,#mermaid-svg-x0HJHKRLlYJ3ogEx .node ellipse,#mermaid-svg-x0HJHKRLlYJ3ogEx .node polygon,#mermaid-svg-x0HJHKRLlYJ3ogEx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-x0HJHKRLlYJ3ogEx .rough-node .label text,#mermaid-svg-x0HJHKRLlYJ3ogEx .node .label text,#mermaid-svg-x0HJHKRLlYJ3ogEx .image-shape .label,#mermaid-svg-x0HJHKRLlYJ3ogEx .icon-shape .label{text-anchor:middle;}#mermaid-svg-x0HJHKRLlYJ3ogEx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-x0HJHKRLlYJ3ogEx .rough-node .label,#mermaid-svg-x0HJHKRLlYJ3ogEx .node .label,#mermaid-svg-x0HJHKRLlYJ3ogEx .image-shape .label,#mermaid-svg-x0HJHKRLlYJ3ogEx .icon-shape .label{text-align:center;}#mermaid-svg-x0HJHKRLlYJ3ogEx .node.clickable{cursor:pointer;}#mermaid-svg-x0HJHKRLlYJ3ogEx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-x0HJHKRLlYJ3ogEx .arrowheadPath{fill:#333333;}#mermaid-svg-x0HJHKRLlYJ3ogEx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-x0HJHKRLlYJ3ogEx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-x0HJHKRLlYJ3ogEx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x0HJHKRLlYJ3ogEx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-x0HJHKRLlYJ3ogEx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x0HJHKRLlYJ3ogEx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-x0HJHKRLlYJ3ogEx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-x0HJHKRLlYJ3ogEx .cluster text{fill:#333;}#mermaid-svg-x0HJHKRLlYJ3ogEx .cluster span{color:#333;}#mermaid-svg-x0HJHKRLlYJ3ogEx 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-x0HJHKRLlYJ3ogEx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-x0HJHKRLlYJ3ogEx rect.text{fill:none;stroke-width:0;}#mermaid-svg-x0HJHKRLlYJ3ogEx .icon-shape,#mermaid-svg-x0HJHKRLlYJ3ogEx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-x0HJHKRLlYJ3ogEx .icon-shape p,#mermaid-svg-x0HJHKRLlYJ3ogEx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-x0HJHKRLlYJ3ogEx .icon-shape .label rect,#mermaid-svg-x0HJHKRLlYJ3ogEx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-x0HJHKRLlYJ3ogEx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-x0HJHKRLlYJ3ogEx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-x0HJHKRLlYJ3ogEx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
是
否
失败
成功
成功
失败
DoProcess
getArgs解析参数
cmd == create?
execRunc直接透传
bundleDirPath为空?
os.Getwd获取当前目录
构造config.json路径
modifySpecFile
安全校验+读取+修改+写回
返回错误
execRunc
syscall.Exec替换为runc
进程变为runc
不会返回
五、完整调用关系图
#mermaid-svg-qUnSMxfYI1KIr82A{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-qUnSMxfYI1KIr82A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-qUnSMxfYI1KIr82A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-qUnSMxfYI1KIr82A .error-icon{fill:#552222;}#mermaid-svg-qUnSMxfYI1KIr82A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-qUnSMxfYI1KIr82A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-qUnSMxfYI1KIr82A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-qUnSMxfYI1KIr82A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-qUnSMxfYI1KIr82A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-qUnSMxfYI1KIr82A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-qUnSMxfYI1KIr82A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-qUnSMxfYI1KIr82A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-qUnSMxfYI1KIr82A .marker.cross{stroke:#333333;}#mermaid-svg-qUnSMxfYI1KIr82A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-qUnSMxfYI1KIr82A p{margin:0;}#mermaid-svg-qUnSMxfYI1KIr82A .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-qUnSMxfYI1KIr82A .cluster-label text{fill:#333;}#mermaid-svg-qUnSMxfYI1KIr82A .cluster-label span{color:#333;}#mermaid-svg-qUnSMxfYI1KIr82A .cluster-label span p{background-color:transparent;}#mermaid-svg-qUnSMxfYI1KIr82A .label text,#mermaid-svg-qUnSMxfYI1KIr82A span{fill:#333;color:#333;}#mermaid-svg-qUnSMxfYI1KIr82A .node rect,#mermaid-svg-qUnSMxfYI1KIr82A .node circle,#mermaid-svg-qUnSMxfYI1KIr82A .node ellipse,#mermaid-svg-qUnSMxfYI1KIr82A .node polygon,#mermaid-svg-qUnSMxfYI1KIr82A .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-qUnSMxfYI1KIr82A .rough-node .label text,#mermaid-svg-qUnSMxfYI1KIr82A .node .label text,#mermaid-svg-qUnSMxfYI1KIr82A .image-shape .label,#mermaid-svg-qUnSMxfYI1KIr82A .icon-shape .label{text-anchor:middle;}#mermaid-svg-qUnSMxfYI1KIr82A .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-qUnSMxfYI1KIr82A .rough-node .label,#mermaid-svg-qUnSMxfYI1KIr82A .node .label,#mermaid-svg-qUnSMxfYI1KIr82A .image-shape .label,#mermaid-svg-qUnSMxfYI1KIr82A .icon-shape .label{text-align:center;}#mermaid-svg-qUnSMxfYI1KIr82A .node.clickable{cursor:pointer;}#mermaid-svg-qUnSMxfYI1KIr82A .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-qUnSMxfYI1KIr82A .arrowheadPath{fill:#333333;}#mermaid-svg-qUnSMxfYI1KIr82A .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-qUnSMxfYI1KIr82A .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-qUnSMxfYI1KIr82A .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qUnSMxfYI1KIr82A .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-qUnSMxfYI1KIr82A .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qUnSMxfYI1KIr82A .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-qUnSMxfYI1KIr82A .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-qUnSMxfYI1KIr82A .cluster text{fill:#333;}#mermaid-svg-qUnSMxfYI1KIr82A .cluster span{color:#333;}#mermaid-svg-qUnSMxfYI1KIr82A 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-qUnSMxfYI1KIr82A .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-qUnSMxfYI1KIr82A rect.text{fill:none;stroke-width:0;}#mermaid-svg-qUnSMxfYI1KIr82A .icon-shape,#mermaid-svg-qUnSMxfYI1KIr82A .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-qUnSMxfYI1KIr82A .icon-shape p,#mermaid-svg-qUnSMxfYI1KIr82A .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-qUnSMxfYI1KIr82A .icon-shape .label rect,#mermaid-svg-qUnSMxfYI1KIr82A .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-qUnSMxfYI1KIr82A .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-qUnSMxfYI1KIr82A .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-qUnSMxfYI1KIr82A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} DoProcess
getArgs
execRunc
modifySpecFile
CheckPath
安全校验
readSpecFile
processDevicesAndHooks
writeSpecFile
CheckFileInfo
文件安全校验
json.Unmarshal
checkVisibleDevice
GetMatchingNpuWorker
addHook
addDevice
addAscendDockerEnv
getValueByDeviceKey
从后往前遍历
parseDevices
纯数字格式
parseAscendDevices
命名格式
getDeviceListFromVisibleValue
removeDuplication
deviceRegx正则匹配
os.Executable
RealFileChecker
dcmi.CreateVDevice
updateEnvAndPostHook
getValueByKey
检查VIRTUAL
addDeviceToSpec
循环每个设备
addUBDevice
addManagerDevice
oci.DeviceFromPath
Spec.Linux.Devices
Spec.Linux.Resources.Devices
os.Stat uburma
os.Stat ummu
addDevicesInDir
GetChipName
GetDeviceTypeByChipName
addAscend310BManagerDevice
addCommonManagerDevice
GetProductType
LookPath docker-runc
LookPath runc
EvalSymlinks
RealFileChecker
ChangeRuntimeLogMode
syscall.Exec