start.go#Action

  1. Action: func(context *cli.Context) error {
  2. if err := checkArgs(context, 1, exactArgs); err != nil {
  3. return err
  4. }
  5. // ********************************** NOTICE ********************************** //
  6. container, err := getContainer(context)
  7. // ********************************** NOTICE ********************************** //
  8. if err != nil {
  9. return err
  10. }
  11. status, err := container.Status()
  12. if err != nil {
  13. return err
  14. }
  15. switch status {
  16. case libcontainer.Created:
  17. // ********************************** NOTICE ********************************** //
  18. return container.Exec()
  19. // ********************************** NOTICE ********************************** //
  20. case libcontainer.Stopped:
  21. return errors.New("cannot start a container that has stopped")
  22. case libcontainer.Running:
  23. return errors.New("cannot start an already running container")
  24. default:
  25. return fmt.Errorf("cannot start a container in the %s state\n", status)
  26. }
  27. },

1) utils_linux.go#getContainer

  1. // getContainer returns the specified container instance by loading it from state
  2. // with the default factory.
  3. func getContainer(context *cli.Context) (libcontainer.Container, error) {
  4. id := context.Args().First()
  5. if id == "" {
  6. return nil, errEmptyID
  7. }
  8. factory, err := loadFactory(context)
  9. if err != nil {
  10. return nil, err
  11. }
  12. return factory.Load(id)
  13. }

1.1) libcontainer/factory_linux,go#LinuxFactory.Load

  1. func (l *LinuxFactory) Load(id string) (Container, error) {
  2. if l.Root == "" {
  3. return nil, newGenericError(fmt.Errorf("invalid root"), ConfigInvalid)
  4. }
  5. //when load, we need to check id is valid or not.
  6. if err := l.validateID(id); err != nil {
  7. return nil, err
  8. }
  9. containerRoot, err := securejoin.SecureJoin(l.Root, id)
  10. if err != nil {
  11. return nil, err
  12. }
  13. // ********************************** NOTICE ********************************** //
  14. state, err := l.loadState(containerRoot, id)
  15. // ********************************** NOTICE ********************************** //
  16. if err != nil {
  17. return nil, err
  18. }
  19. r := &nonChildProcess{
  20. processPid: state.InitProcessPid,
  21. processStartTime: state.InitProcessStartTime,
  22. fds: state.ExternalDescriptors,
  23. }
  24. c := &linuxContainer{
  25. initProcess: r,
  26. initProcessStartTime: state.InitProcessStartTime,
  27. id: id,
  28. config: &state.Config,
  29. initPath: l.InitPath,
  30. initArgs: l.InitArgs,
  31. criuPath: l.CriuPath,
  32. newuidmapPath: l.NewuidmapPath,
  33. newgidmapPath: l.NewgidmapPath,
  34. cgroupManager: l.NewCgroupsManager(state.Config.Cgroups, state.CgroupPaths),
  35. root: containerRoot,
  36. created: state.Created,
  37. }
  38. c.state = &loadedState{c: c}
  39. if err := c.refreshState(); err != nil {
  40. return nil, err
  41. }
  42. if intelrdt.IsCatEnabled() || intelrdt.IsMbaEnabled() {
  43. c.intelRdtManager = l.NewIntelRdtManager(&state.Config, id, state.IntelRdtPath)
  44. }
  45. return c, nil
  46. }
  47. func (l *LinuxFactory) validateID(id string) error {
  48. if !idRegex.MatchString(id) || string(os.PathSeparator)+id != utils.CleanPath(string(os.PathSeparator)+id) {
  49. return newGenericError(fmt.Errorf("invalid id format: %v", id), InvalidIdFormat)
  50. }
  51. return nil
  52. }

1.1.1) libcontainer/factory_linux.go#LinuxFactory.loadState

  1. func (l *LinuxFactory) loadState(root, id string) (*State, error) {
  2. stateFilePath, err := securejoin.SecureJoin(root, stateFilename)
  3. if err != nil {
  4. return nil, err
  5. }
  6. f, err := os.Open(stateFilePath)
  7. if err != nil {
  8. if os.IsNotExist(err) {
  9. return nil, newGenericError(fmt.Errorf("container %q does not exist", id), ContainerNotExists)
  10. }
  11. return nil, newGenericError(err, SystemError)
  12. }
  13. defer f.Close()
  14. var state *State
  15. if err := json.NewDecoder(f).Decode(&state); err != nil {
  16. return nil, newGenericError(err, SystemError)
  17. }
  18. return state, nil
  19. }
  20. const stateFilename = "state.json"

2) libcontainer/container_linux.go#linuxContainer.Exec

  1. func (c *linuxContainer) Exec() error {
  2. c.m.Lock()
  3. defer c.m.Unlock()
  4. // ********************************** NOTICE ********************************** //
  5. return c.exec()
  6. // ********************************** NOTICE ********************************** //
  7. }
  8. const execFifoFilename = "exec.fifo"
  9. func (c *linuxContainer) exec() error {
  10. path := filepath.Join(c.root, execFifoFilename)
  11. fifoOpen := make(chan struct{})
  12. select {
  13. case <-awaitProcessExit(c.initProcess.pid(), fifoOpen):
  14. return errors.New("container process is already dead")
  15. case result := <-awaitFifoOpen(path):
  16. close(fifoOpen)
  17. if result.err != nil {
  18. return result.err
  19. }
  20. f := result.file
  21. defer f.Close()
  22. if err := readFromExecFifo(f); err != nil {
  23. return err
  24. }
  25. return os.Remove(path)
  26. }
  27. }

2.1) libcontainer/container_linux.go#awaitFifoOpen

如果是start,那么runc会以只读方式打开fifo管道,读取内容,如果长度大于0,则读取到Create流程中最后写入的“0”,也同时恢复阻塞了Create的init进程,执行最后调用用户进程部分。

  1. func awaitFifoOpen(path string) <-chan openResult {
  2. fifoOpened := make(chan openResult)
  3. go func() {
  4. f, err := os.OpenFile(path, os.O_RDONLY, 0)
  5. if err != nil {
  6. fifoOpened <- openResult{err: newSystemErrorWithCause(err, "open exec fifo for reading")}
  7. return
  8. }
  9. fifoOpened <- openResult{file: f}
  10. }()
  11. return fifoOpened
  12. }