runc源码分析

runc代码目录结构

├── 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目录结构

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命令框架类似)。添加了多个重要的子命令。

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的核心逻辑。

// 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处理代码

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)
}

待完善


最后修改 June 11, 2025: update k8s (3e78c6a)