Sheet 1
runc
  create
    setupSpec(context)
    startContainer(context, spec, true)
      createContainer(context, id, spec)
      r := &runner{
enableSubreaper: !context.Bool("no-subreaper"),
shouldDestroy: true,
container: container,
listenFDs: listenFDs,
consoleSocket: context.String("console-socket"),
detach: context.Bool("detach"),
pidFile: context.String("pid-file"),
create: create,
}
      r.run(&spec.Process)
        newProcess(*config)
        startFn := r.container.Start
if !r.create {
startFn = r.container.Run
}
        handler := newSignalHandler(r.enableSubreaper)
        tty, err := setupIO(process, rootuid, rootgid, config.Terminal, detach)
        startFn(process)
          func (c *linuxContainer) Run(process *Process) error {
c.start(process, status == Stopped)
}
            parent, err := c.newParentProcess(process, isInit)
              parentPipe, childPipe, err := newPipe()
              cmd, err := c.commandTemplate(p, childPipe, rootDir)
                func (c *linuxContainer) commandTemplate(p *Process, childPipe, rootDir *os.File) (*exec.Cmd, error) {
cmd := exec.Command(c.initArgs[0], c.initArgs[1:]...)
。。。
cmd.ExtraFiles = append(p.ExtraFiles, childPipe, rootDir)
cmd.Env = append(cmd.Env,
fmt.Sprintf("_LIBCONTAINER_INITPIPE=%d", stdioFdCount+len(cmd.ExtraFiles)-2),
fmt.Sprintf("_LIBCONTAINER_STATEDIR=%d", stdioFdCount+len(cmd.ExtraFiles)-1))
。。。
return cmd, nil
}
              if !doInit {
return c.newSetnsProcess(p, cmd, parentPipe, childPipe, rootDir)
}
return c.newInitProcess(p, cmd, parentPipe, childPipe, rootDir)
                newInitProcess{}
                cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard))
                data, err := c.bootstrapData(c.config.Namespaces.CloneFlags(), nsMaps)
                  r := nl.NewNetlinkRequest(int(InitMsg), 0)
                  r.AddData(&Int32msg{
Type: CloneFlagsAttr,
Value: uint32(cloneFlags), //新建ns
})
                  r.AddData(
&Bytemsg{Type: NsPathsAttr,
Value: []byte(strings.Join(nsPaths, ",")),
})
values格式：user:/proc/1/ns/net,ipc:/proc/556/ns/net
按顺序：user,ipc,uts,net,pid,ns排列
                  r.AddData(&Bytemsg{
Type: UidmapAttr,
Value: b,
})
                  r.AddData(&Bytemsg{
Type: GidmapAttr,
Value: b,
})
                  if !pid.Get(capability.EFFECTIVE, capability.CAP_SETGID) {
r.AddData(&Boolmsg{
Type: SetgroupAttr,
Value: true,
})
}
                p.consoleChan = make(chan *os.File, 1)
                return &initProcess{
cmd: cmd, // "/proc/self/exec init"
childPipe: childPipe,
parentPipe: parentPipe,
manager: c.cgroupManager,
config: c.newInitConfig(p),
container: c,
process: p, //spec.Process用户进程
bootstrapData: data,
sharePidns: sharePidns,
rootDir: rootDir,
}, nil
            parent.start() -- initProcess.start()
              err := p.cmd.Start() // init进程开始启动 /proc/self/exe init
p.process.ops = p //p是initProcess结构体
                _ "github.com/opencontainers/runc/libcontainer/nsenter"
                  libcontainer/nsenter/nsenter_gccgo.go-->libcontainer/nsenter/nsexec.c
                    pipenum=getenv("_LIBCONTAINER_INITPIPE") //拿到的是childpipe
                    /* Parse all of the netlink configuration. */
nl_parse(pipenum, &config); //解析bootstrap data
                      read(fd, &hdr, NLMSG_HDRLEN);
                      switch (nlattr->nla_type) {
case CLONE_FLAGS_ATTR:
config->cloneflags = readint32(current);
break;
case NS_PATHS_ATTR:
config->namespaces = current;
config->namespaces_len = payload_len;
break;
case UIDMAP_ATTR:
config->uidmap = current;
config->uidmap_len = payload_len;
break;
case GIDMAP_ATTR:
config->gidmap = current;
config->gidmap_len = payload_len;
break;
case SETGROUP_ATTR:
config->is_setgroup = readint8(current);
break;
                    socketpair(AF_LOCAL, SOCK_STREAM, 0, syncpipe) //syncpipe是用来跟孙子进程通信的
                    switch (setjmp(env))
                      case JUMP_PARENT (第一阶段，父进程里面)
                        prctl(PR_SET_NAME, (unsigned long) "runc:[0:PARENT]", 0, 0, 0);
                        child = clone_parent(&env, JUMP_CHILD);
                          clone(child_func, ca.stack_ptr, CLONE_PARENT | SIGCHLD, &ca);
CLONE_PARENT代表新进程的父进程==init进程父进程==runc create
                            static int child_func(void *arg)
{
struct clone_t *ca = (struct clone_t *)arg;
longjmp(*ca->env, ca->jmpval);
}
//跳到CLONE_CHILD那里了。
                        while (ready < 2) {
read(syncfd, &s, sizeof(s))
switch (s){见右边}
}
                          case SYNC_ERR
                          case SYNC_USERMAP_PLS
                            update_setgroups(child, SETGROUPS_ALLOW)
                            update_uidmap(child, config.uidmap, config.uidmap_len);
update_gidmap(child, config.gidmap, config.gidmap_len);
                            s = SYNC_USERMAP_ACK
write(syncfd, &s, sizeof(s))
                          case SYNC_USERMAP_ACK
                            should never happen!!
                          case SYNC_RECVPID_PLS
                            pid_t old = child
                            read(syncfd, &child, sizeof(child))
                            s = SYNC_RECVPID_ACK;
write(syncfd, &s, sizeof(s))
                            ready++;
                          case SYNC_RECVPID_ACK
                            should never happen!!
                          case SYNC_CHILD_READY
                            ready++;
                        snprintf(buf, JSON_MAX, "{\"pid\": %d}\n", child);
                        write(pipenum, buf, len) // pid写回父进程
                      case JUMP_CHILD
                        syncfd = syncpipe[0]; //写端
                        prctl(PR_SET_NAME, (unsigned long) "runc:[1:CHILD]", 0, 0, 0)
                        join_namespaces(config.namespaces)
                          setns(ns.fd, ns.ns) //按顺序加入指定的ns内
                        unshare(config.cloneflags) //创建新的ns
                        if (config.cloneflags & CLONE_NEWUSER) {
s = SYNC_USERMAP_PLS;
write(syncfd, &s, sizeof(s) //要求父进程建立user-ns map
read(syncfd, &s, sizeof(s) //判断response是不是SYNC_USERMAP_ACK
                        child = clone_parent(&env, JUMP_INIT); // pid ns必须再fork一次才能使子进程生效，unshare或setns无法改变当前进程pid ns。 跳到JUMP_INIT了
                        s = SYNC_RECVPID_PLS;
write(syncfd, &s, sizeof(s)
write(syncfd, &child, sizeof(child) // 把真正的init进程的pid传给parent
                        read(syncfd, &s, sizeof(s)
if (s != SYNC_RECVPID_ACK) //判断返回值是SYNC_RECVPID_ACK
                      case JUMP_INIT
                        prctl(PR_SET_NAME, (unsigned long) "runc:[2:INIT]", 0, 0, 0);
                        setsid()
                        setuid(0)
                        setgid(0)
                        setgroups(0, NULL)
                        s = SYNC_CHILD_READY;
write(syncfd, &s, sizeof(s))
                factory, _ := libcontainer.New("")
                factory.StartInitialization()
                  pipefd=os.Getenv("_LIBCONTAINER_INITPIPE")转int
rootfd=os.Getenv("_LIBCONTAINER_STATEDIR")转int
pipe = os.NewFile(uintptr(pipefd), "pipe")
it = initType(os.Getenv("_LIBCONTAINER_INITTYPE")) //standard
                  i, err = newContainerInit(it, pipe, rootfd)
                    var config *initConfig
json.NewDecoder(pipe).Decode(&config) //从父进程接受initConfig
                    populateProcessEnvironment //加载所有的环境变量
                      os.Setenv(p[0], p[1])
                    switch t {
case initSetns:
return &linuxSetnsInit{
pipe: pipe,
config: config,
}, nil
case initStandard:
return &linuxStandardInit{
pipe: pipe,
parentPid: syscall.Getppid(),
config: config,
stateDirFD: stateDirFD,
}, nil
}
                  return i.Init()
                    if !l.config.Config.NoNewKeyring {...}
                    setupNetwork(l.config)
                    setupRoute(l.config.Config)
                    label.Init()
                    if l.config.Config.Namespaces.Contains(configs.NEWNS) {
prepareRootfs(l.pipe, l.config.Config);
}
                      prepareRoot(config);
                        flag := syscall.MS_SLAVE | syscall.MS_REC
if config.RootPropagation != 0 {
flag = config.RootPropagation
}
syscall.Mount("", "/", "", uintptr(flag), "")
                        rootfsParentMountPrivate(config.Rootfs)
                        syscall.Mount(config.Rootfs, config.Rootfs, "bind", syscall.MS_BIND|syscall.MS_REC, "")
                        for _, m := range config.Mounts {
// exec PremountCmds
mountToRootfs(m, config.Rootfs, config.MountLabel);
// exec PostmountCmds
}
                        if setupDev {
createDevices(config)
setupPtmx(config)
setupDevSymlinks(config.Rootfs)
}
                        syncParentHooks(pipe)
                        syscall.Chdir(config.Rootfs)
                        pivotRoot(config.Rootfs) // 切换rootfs了！！
                    l.config.CreateConsole {
setupConsole(l.consoleSocket, l.config, true)
system.Setctty()
}
                    if l.config.Config.Namespaces.Contains(configs.NEWNS) {
finalizeRootfs(l.config.Config)
}
                    syscall.Sethostname([]byte(hostname))
                    apparmor.ApplyProfile(l.config.AppArmorProfile)
                    label.SetProcessLabel(l.config.ProcessLabel)
                    writeSystemProperty(key, value) //设置sysctl
                    for _, path := range l.config.Config.ReadonlyPaths {
readonlyPath(path)
}
                    if l.config.NoNewPrivileges {
system.Prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
}
                    syncParentReady(l.pipe)
                    seccomp.InitSeccomp(l.config.Config.Seccomp)
                    finalizeNamespace(l.config)
                    syscall.Openat(l.stateDirFD, execFifoFilename, os.O_WRONLY|syscall.O_CLOEXEC, 0)
syscall.Write(fd, []byte("0"))
// 等待exec！！ 从exec.fifo里面写数据，runc start会执行这一步的exec操作，读pipe数据
                    seccomp.InitSeccomp(l.config.Config.Seccomp)
                    syscall.Close(l.stateDirFD)
                    syscall.Exec(name, l.config.Args[0:], os.Environ())
// 真正执行用户程序了！容器start了！
              io.Copy(p.parentPipe, p.bootstrapData)
              p.execSetns()
                status, err := p.cmd.Process.Wait()
                var pid *pid
json.NewDecoder(p.parentPipe).Decode(&pid) //获得pid
                process, err := os.FindProcess(pid.Pid)
p.cmd.Process = process
p.process.ops = p
              p.setExternalDescriptors(fds) //fds是fd0,1,2链接到的文件。
              // Do this before syncing with child so that no children
// can escape the cgroup
p.manager.Apply(p.pid())
              p.createNetworkInterfaces()
              p.sendConfig()
              parseSync(p.parentPipe, func(sync *syncT) error {
switch sync.Type {
case procReady:
case procHooks:
default:
}
                case procHooks:
// run Prestart Hooks
                case procReady:
                  p.manager.Set(p.config.Config)
                  setOomScoreAdj(p.config.Config.OomScoreAdj, p.pid())
                  setupRlimits(p.config.Rlimits, p.pid())
                  setupRlimits(p.config.Rlimits, p.pid())
                  if !p.config.Config.Namespaces.Contains(configs.NEWNS) {
// run prestart hooks
}
              syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR)
            if isInit {
--更新container status
--运行poststart hook
}