start.go#Action
Action: func(context *cli.Context) error {if err := checkArgs(context, 1, exactArgs); err != nil {return err}// ********************************** NOTICE ********************************** //container, err := getContainer(context)// ********************************** NOTICE ********************************** //if err != nil {return err}status, err := container.Status()if err != nil {return err}switch status {case libcontainer.Created:// ********************************** NOTICE ********************************** //return container.Exec()// ********************************** NOTICE ********************************** //case libcontainer.Stopped:return errors.New("cannot start a container that has stopped")case libcontainer.Running:return errors.New("cannot start an already running container")default:return fmt.Errorf("cannot start a container in the %s state\n", status)}},
1) utils_linux.go#getContainer
// getContainer returns the specified container instance by loading it from state// with the default factory.func getContainer(context *cli.Context) (libcontainer.Container, error) {id := context.Args().First()if id == "" {return nil, errEmptyID}factory, err := loadFactory(context)if err != nil {return nil, err}return factory.Load(id)}
1.1) libcontainer/factory_linux,go#LinuxFactory.Load
func (l *LinuxFactory) Load(id string) (Container, error) {if l.Root == "" {return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid)}//when load, we need to check id is valid or not.if err := l.validateID(id); err != nil {return nil, err}containerRoot, err := securejoin.SecureJoin(l.Root, id)if err != nil {return nil, err}// ********************************** NOTICE ********************************** //state, err := l.loadState(containerRoot, id)// ********************************** NOTICE ********************************** //if err != nil {return nil, err}r := &nonChildProcess{processPid: state.InitProcessPid,processStartTime: state.InitProcessStartTime,fds: state.ExternalDescriptors,}c := &linuxContainer{initProcess: r,initProcessStartTime: state.InitProcessStartTime,id: id,config: &state.Config,initPath: l.InitPath,initArgs: l.InitArgs,criuPath: l.CriuPath,newuidmapPath: l.NewuidmapPath,newgidmapPath: l.NewgidmapPath,cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths),root: containerRoot,created: state.Created,}c.state = &loadedState{c: c}if err := c.refreshState(); err != nil {return nil, err}if intelrdt.IsCatEnabled() || intelrdt.IsMbaEnabled() {c.intelRdtManager = l.NewIntelRdtManager(&state.Config, id, state.IntelRdtPath)}return c, nil}func (l *LinuxFactory) validateID(id string) error {if !idRegex.MatchString(id) || string(os.PathSeparator)+id != utils.CleanPath(string(os.PathSeparator)+id) {return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)}return nil}
1.1.1) libcontainer/factory_linux.go#LinuxFactory.loadState
func (l *LinuxFactory) loadState(root, id string) (*State, error) {stateFilePath, err := securejoin.SecureJoin(root, stateFilename)if err != nil {return nil, err}f, err := os.Open(stateFilePath)if err != nil {if os.IsNotExist(err) {return nil, newGenericError(fmt.Errorf("container %q does not exist", id), ContainerNotExists)}return nil, newGenericError(err, SystemError)}defer f.Close()var state *Stateif err := json.NewDecoder(f).Decode(&state); err != nil {return nil, newGenericError(err, SystemError)}return state, nil}const stateFilename = "state.json"
2) libcontainer/container_linux.go#linuxContainer.Exec
func (c *linuxContainer) Exec() error {c.m.Lock()defer c.m.Unlock()// ********************************** NOTICE ********************************** //return c.exec()// ********************************** NOTICE ********************************** //}const execFifoFilename = "exec.fifo"func (c *linuxContainer) exec() error {path := filepath.Join(c.root, execFifoFilename)fifoOpen := make(chan struct{})select {case <-awaitProcessExit(c.initProcess.pid(), fifoOpen):return errors.New("container process is already dead")case result := <-awaitFifoOpen(path):close(fifoOpen)if result.err != nil {return result.err}f := result.filedefer f.Close()if err := readFromExecFifo(f); err != nil {return err}return os.Remove(path)}}
2.1) libcontainer/container_linux.go#awaitFifoOpen
如果是start,那么runc会以只读方式打开fifo管道,读取内容,如果长度大于0,则读取到Create流程中最后写入的“0”,也同时恢复阻塞了Create的init进程,执行最后调用用户进程部分。
func awaitFifoOpen(path string) <-chan openResult {fifoOpened := make(chan openResult)go func() {f, err := os.OpenFile(path, os.O_RDONLY, 0)if err != nil {fifoOpened <- openResult{err: newSystemErrorWithCause(err, "open exec fifo for reading")}return}fifoOpened <- openResult{file: f}}()return fifoOpened}
