runc源码分析

Posted by 胡伟煌 on 2025-06-10

runc代码目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
├── create.go  # createCommand
├── delete.go # deleteCommand
├── events.go # eventsCommand
├── exec.go # execCommand
├── features.go # featuresCommand
├── kill.go # killCommand
├── libcontainer # 核心实现逻辑
├── list.go # listCommand
├── main.go # main函数
├── pause.go # pauseCommand
├── ps.go # psCommand
├── restore.go # restoreCommand
├── run.go # runCommand
├── spec.go # specCommand
├── start.go # startCommand
├── state.go # stateCommand
├── update.go # updateCommand
├── utils_linux.go # startContainer

libcontainer目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
libcontainer
├── apparmor
├── capabilities
├── cgroups
├── configs
├── console_linux.go
├── container.go
├── container_linux.go
├── container_linux_test.go
├── criu_opts_linux.go
├── devices
├── error.go
├── factory_linux.go
├── factory_linux_test.go
├── init_linux.go
├── integration
├── intelrdt
├── keys
├── logs
├── message_linux.go
├── mount_linux.go
├── network_linux.go
├── notify_linux.go
├── notify_linux_test.go
├── notify_v2_linux.go
├── nsenter
├── process.go
├── process_linux.go
├── restored_process.go
├── rootfs_linux.go
├── rootfs_linux_test.go
├── seccomp
├── setns_init_linux.go
├── specconv
├── standard_init_linux.go
├── state_linux.go
├── state_linux_test.go
├── stats_linux.go
├── sync.go
├── system
├── user
├── userns
└── utils

Main函数

runc的代码仓库主要使用了github.com/urfave/cli的命令框架(该框架与cobra命令框架类似)。添加了多个重要的子命令。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func main() {
app := cli.NewApp()
app.Name = "runc"
app.Usage = usage

app.Commands = []cli.Command{
checkpointCommand,
createCommand,
deleteCommand,
eventsCommand,
execCommand,
killCommand,
listCommand,
pauseCommand,
psCommand,
restoreCommand,
resumeCommand,
runCommand,
specCommand,
startCommand,
stateCommand,
updateCommand,
featuresCommand,
}

runCommand

runCommand为例分析子命令的调用流程。

github.com/urfave/cli命令框架代码格式:

创建一个Command结构体,包含:

  • Name:命名名称

  • Usage:使用说明

  • Description:描述命令信息

  • Flags:解析参数

  • Action: command run的核心逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// default action is to start a container
var runCommand = cli.Command{
Name: "run",
Usage: "create and run a container",
// 删除描述信息
ArgsUsage: ``,
Description: ``,
Flags: []cli.Flag{
cli.StringFlag{
Name: "bundle, b",
Value: "",
Usage: `path to the root of the bundle directory, defaults to the current directory`,
},
// 删除多余的FLAG代码
},
Action: func(context *cli.Context) error {
if err := checkArgs(context, 1, exactArgs); err != nil {
return err
}
// 核心代码,启动容器
status, err := startContainer(context, CT_ACT_RUN, nil)
if err == nil {
// exit with the container's exit status so any external supervisor is
// notified of the exit with the correct exit status.
os.Exit(status)
}
return fmt.Errorf("runc run failed: %w", err)
},
}

startContainer

启动容器的流程:

  1. setup spec信息。

  2. 基于spec信息创建container。

  3. 通过runner启动进程。

删除error处理代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
func startContainer(context *cli.Context, action CtAct, criuOpts *libcontainer.CriuOpts) (int, error) {
if err := revisePidFile(context); err != nil {
return -1, err
}
spec, err := setupSpec(context)

id := context.Args().First()

notifySocket := newNotifySocket(context, os.Getenv("NOTIFY_SOCKET"), id)
if notifySocket != nil {
notifySocket.setupSpec(spec)
}

container, err := createContainer(context, id, spec)

if notifySocket != nil {
if err := notifySocket.setupSocketDirectory(); err != nil {
return -1, err
}
if action == CT_ACT_RUN {
if err := notifySocket.bindSocket(); err != nil {
return -1, err
}
}
}

// Support on-demand socket activation by passing file descriptors into the container init process.
listenFDs := []*os.File{}
if os.Getenv("LISTEN_FDS") != "" {
listenFDs = activation.Files(false)
}

r := &runner{
enableSubreaper: !context.Bool("no-subreaper"),
shouldDestroy: !context.Bool("keep"),
container: container,
listenFDs: listenFDs,
notifySocket: notifySocket,
consoleSocket: context.String("console-socket"),
detach: context.Bool("detach"),
pidFile: context.String("pid-file"),
preserveFDs: context.Int("preserve-fds"),
action: action,
criuOpts: criuOpts,
init: true,
}
return r.run(spec.Process)
}

待完善



支付宝打赏 微信打赏

赞赏一下