1、// runc/create.go

Action: func(context *cli.Context) error

首先调用spec, err := setupSpec(context)加载配置文件config.json的内容。之后调用status, err := startcontainer(context, spec, true)进行容器的创建工作,其中最后一个布尔型的参数为true,表示进行容器的创建。

2、// runc/utils_linux.go

func startContainer(context *cli.Context, spec *specs.Spec, create bool) (int ,error)

首先调用container, err := createContainer(context, id, spec)创建容器, 之后填充runner结构r,如下所示:

r := &runner{

  enableSubreaper:     !context.Bool("no-subreaper"),

  shouldDestroy:       true,

  container:        container,

  listenFDs:        listenFDs,    // if os.Getenv("LISTEN_FDS") != "" { listenFDs = activation.Files(false) }

  console:         context.String("console") 

  detach:         detach,

  pidFile:         context.String("pid-file")

  create:         create,

}

最后调用r.run(&spec.Process)。

--------------------------------------------------------------------开始创建容器对象---------------------------------------------------------------------

3、// runc/utils_linux.go

func createContainer(context *cli.Context, id string, spec *specs.Spec) (libcontainer.Container, error)

首先调用config, err := specconv.CreateLibcontainerConfig(&specconv.CreateOpts{...})将配置转换为符合libcontainer要求的配置格式。之后调用factory, err := loadFactory(context)加载factory对象。最后,调用factory.Create(id, config)创建一个libcontainer的container对象。

4、// runc/utils_linux.go

// loadFactory returns the configured factory instance for execing containers

func loadFactory(context *cli.Context) (libcontainer.Factory, error)

首先获取root dir的绝对路径abs(默认为/run/runc,用于存储容器的状态)以及cgroupManager,最后调用libcontainer.New(abs, cgroupManager, libcontainer.CriuPath(context.GlobalString("criu")))来生产一个Factory实例。

5、// runc/libcontainer/factory_linux.go

// New returns a linux based container factory based in the root directory and configures the factory with the provided option funcs

func New(root string, options ...func(*LinuxFactory) error) (Factory, error)

首先填充一个LinuxFactory对象,如下所示:

l := &LinuxFactory{

  Root:    root,

  InitArgs:   []string{"/proc/self/exec", "init"},

  Validator:    validate.New(),

  CriuPath:    "criu",

}

接着调用Cgroupfs(l)将l的NewCgroupsManager设置为利用本地的cgroups filesystem implementation来创建和管理cgroups(相对应的,也可以利用systemd来)。再遍历options,来对l进行选择性配置。从这个函数的调用处可知,主要配置了cgroups和criupath。最后,返回l。

6、// runc/libcontainer/factory_linux.go

func (l *LinuxFactory) Create(id string, config *configs.Config) (Container)

该函数首先对id和config进行验证,接着获取uid, gid, containerRoot并且创建containerRoot(默认为/run/runc/container-id)。之后再创建一个FIFO文件,填充一个linuxContainer对象,如下所示:

c := &linuxContainer{

  id:       id,

  root:      containerRoot,

  config:     config,

  initArgs:      l.InitArgs,

  criuPath:     l.CriuPath,

  cgroupManager: l.NewCgroupsManager(config.Cgroups, nil)

}

c.state = &stoppedState{c: c}

最后,return c

--------------------------------------------------------------------创建容器对象结束---------------------------------------------------------------------

7、// runc/utils_linux.go

func (r *runner) run(config *specs.Process) (int ,error)

(1)、调用process, err := newProcess(*config),根据config配置一个libcontainer.Process对象。如果r.listenFDs的数目不为0的话,扩展process.Env和process.ExtraFiles,然后获取rootuid和rootgid。

(2)、接着调用tty, err := setupIO(process, rootuid, rootgid, r.console, config.Terminal, r.detach || r.create)设置和process的IO。

(3)、调用handler := newSignalHandler(tty, r.enableSubreaper)以及之后的status, err := handler.forward(process)对来自process的SIGNAL进行处理(如果没有detach并且不是create容器的话)。

(4)、调用startFn(process),如果是create容器的话,那么startFn := r.container.Start,否则startFn := r.container.Run。因为我们是对create 容器进行分析 ,因此startFn为r.container.Start。

(5)、调用tty.ClosePostStart()关闭已经传递给容器的fds。

8、// runc/libcontainer/container_linux.go

func (c *linuxContainer) Start(process *Process)

这个函数很简单,先调用status, err := c.currentStatus()获取容器的当前状态,最后调用c.start(process, status == Stopped),在create时,此时容器的状态确实为Stopped

9、// runc/libcontainer/container_linux.go

func (c *linuxContainer) start(process *Process, isInit bool) error

首先调用parent, err := c.newParentProcess(process, isInit)获取一个parentProcess的接口类型。然后再调用parent.start()启动进程。最后,如果isInit为true,即create容器时,c.state = &createdState{c: c}, 假如c.config.Hooks != nil的话,执行一些c.config.Hooks.Poststart。(在基本的runc spec生成的config.json中hooks为空)

10、// runc/libcontainer/container_linux.go

func (c *linuxContainer) newParentProcess(p *Process, doInit bool) (parentProcess, error)

调用parentPipe, childPipe, err := newPipe()生成一个管道,接着打开rootDir, err  = os.Open(c.root),即打开/run/runc/container-id。然后调用cmd, err := c.commandTemplate(p, childPipe, rootDir)配置启动命令。commandTemplate中最重要的内容为

(1)、添加了两个ExtraFiles:cmd.ExtraFiles = append(p.ExtraFiles, childPipe, rootDir)

(2)、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))

最后,若为create container,则调用c.newInitProcess(p, cmd, parentPipe, childPipe, rootDir),否则调用c.newSetnsProcess(p, cmd, parentPipe, childPipe, rootDir)

11、// runc/libcontainer/container_linux.go

func (c *linuxContainer) newInitProcess(p *Process, cmd *exec.Cmd, parentPipe, childPipe, rootDir *os.File) (*initProcess, error)

(1)、首先扩展cmd.Env = append(cmd.Env, "_LIBCONTAINER_INITTYPE="+string(initStandard)) (initStandard是一个值为"standard"的string)。

(2)、接着获取config.Namespace的nsMaps,类型为map[configs.NamespaceType]string。再调用_, sharePidns := nsMaps[configs.NEWPID]

(3)、调用data, err := c.bootstrapData(c.config.Namespaces.CloneFlags(), nsMaps, "")

(4)、最后返回: return &initProcess {
  cmd:    cmd,

  childPipe:  childPipe,

  parentPipe:  parentPipe,

  manager:   c.cgroupManager,

  config:     c.newInitConfig(p),    // 根据c和p填充结构initConfig

  container:   c,

  process:      p,

  bootstrapData: data,

  sharePidns:   sharePidns,

  rootDir:     rootDir,

}

12、// runc/libcontainer/container_linux.go

// bootstrapData encodes the necessary data in netlink binary format as a io.Reader

// Consumer can write the data to a bootstrap program such as one that uses nsenter package to bootsrap the container's init process correctly

// with correct namespaces, uid/gid, mapping etc

func (c *linuxContainer) bootstrapData(cloneFlags uintptr, nsMaps map[configs.NamespaceType]string, consolePath) (io.Reader, error)

先创建一个利用r := nl.NewlinkRequest(int(InitMsg), 0)创建一个netlink message。之后,将cloneFlags,console path, namespace path等信息写入。最后,return bytes.NewReader(r.Serialize())

13、// runc/libcontainer/process_linux.go

(1)、调用p.cmd.Start启动runc init

(2)、io.Copy(p.parentPipe, p.bootstrapData) // 将bootstrapData的数据传入runc init

(3)、p.execSetns()   // 获取容器init进程的pid,process = os.FindProcess(pid.Pid), p.cmd.Process = process, p.process.ops = p

(4)、fds, err := getPipeFds(p.pid()) // save the standard descriptor names before the container can potentially move them
(5)、p.setExternalDescriptors(fds)

(6)、p.manager.Apply(p.pid()) // do this before syncing with child so that no children can escape the cgroup

(7)、p.createNetworkInterfaces() // creating network interfaces

(8)、p.sendConfig() // sending config to init process, utils.WriteJSON(p.parentPipe, p.config)

(9)、进入loop,从p.parenPipe中获取同步状态procSync

loop :

  for {

    dec.Decode(&procSync)

    switch procSync.Type {

    case procReady:

      ... // set oom_score_adj, set rlimits, call prestart hooks

      utils.WriteJSON(p.parentPipe, syncT{procRun}) // reading syncT run type

      sentRun = true

    case procHooks:

      ...

    case procError:

      ...
    }

  }

  ....

  syscall.Shutdown(int(p.parentPipe.Fd()), syscall.SHUT_WR)

-------------------------------------------------------------------------runc init 执行流程----------------------------------------------------------

1、// main_unix.go

Action: func(context *cli.Context) error

  factory, _ := libcontainer.New("")

  factory.StartInitialization()

2、// runc/libcontainer/factory_linux.go

// StartInitialization loads a container by opening fd from the parent to read the configuration and state

func (l *LinuxFactory) StartInitialization() (err error)

这个函数很简单,从"_LIBCONTAINER_INITPIPE"等环境变量中获取pipefd, rootfd(/run/runc/container-id), it(初始化类型,create中为standard),接着调用i, err = newContainerInit(it, pipe, rootfd),最后调用i.Init()

3、// runc/libcontainer/init_linux.go

func newContainerInit(t initType, pipe *os.File, stateDirFD int) (inter, error)

该函数很简单,先调用json.NewDecoder(pipe).Decode(&config)中从管道中获取配置信息,再调用populateProcessEnvironment(config.Env)将config中的环境变量加载到当前进程中。最后,根据initType的不同,当t为initSetns时,返回&linuxSetnsInit{config: config}。在create container时,则返回&linuxStandardInit{pipe: pipe, parentPid: syscall.Getppid(), config: config, stateDirFD: stateDirFD}

4、// runc/libcontainer/standard_init_linux.go

func (l *linuxStandardInit) Init() error

(1)、该函数先处理l.config.Config.NoNewKeyring,l.config.Console, setupNetwork, setupRoute, label.Init()

(2)、if l.config.Config.Namespaces.Contains(configs.NEWNS) -> setupRootfs(l.config.Config, console, l.pipe)

(3)、设置hostname, apparmor.ApplyProfile(...), label.SetProcessLabel(...),l.config.Config.Sysctl

(4)、调用remountReadonly(path)重新挂载ReadonlyPaths,在配置文件中为/proc/asound,/proc/bus, /proc/fs等等

(5)、调用maskPath(path)设置maskedPaths,pdeath := system.GetParentDeathSignal(), 处理l.config.NoNewPrivileges

(6)、调用syncParentReady(l.pipe) // 告诉父进程容器可以执行Execv了, 从父进程来看,create已经完成了

(7)、处理l.config.Config.Seccomp 和 l.config.NoNewPrivileges, finalizeNamespace(l.config),pdeath.Restore(), 判断syscall.Getppid()和l.parentPid是否相等,找到name, err := exec.Lookpath(l.config.Args[0]),最后l.pipe.Close(),init完成。此时create 在子进程中也完成了。

(8)、fd, err := syscall.Openat(l.stateDirFD, execFifoFilename, os.O_WRONLY|syscall.O_CLOEXEC, 0) ---> wait for the fifo to be opened on the other side before exec'ing the user process,其实此处就是在等待start命令。之后,再往fd中写一个字节,用于同步:syscall.Write(fd, []byte("0"))

(9)、调用syscall.Exec(name, l.config.Args[0:], os.Environ())执行容器命令

runc create container 流程分析的更多相关文章

  1. runc start container流程分析

    1.runc/start.go Action: func(context *cli.Context) error 该函数首先调用container, err := getContainer(conte ...

  2. runv start container 流程分析

    1.runv/start.go func startContainer(context *cli.Context, container, address string, config *spec.Sp ...

  3. docker启动报错解决及分析(Cannot create container for service *******: cannot mount volume over existing file, file exists /var/lib/docker/overlay2/)

    现象: Cannot create container for service *******: cannot mount volume over existing file, file exists ...

  4. u-boot 流程分析

    u-boot 介绍: 对于计算机来说 , 从一开始上机通电是无法直接启动操作系统的 , 这中间需要一个引导过程 , 嵌入式Linux系统同样离不开引导程序 ,  这个启动程序就叫启动加载程序(Boot ...

  5. ofbiz进击 。 ofbiz 退货流程(包含获取可退货项流程分析 以及 取消退货项的过程分析)

    根据订单获取可退货项流程分析 退货的时候,调用 services_return.xml 中的获取可进行退货的退货项  getReturnableItems  ,该服务调用了Java类 org.ofbi ...

  6. Android7.0 Phone应用源码分析(一) phone拨号流程分析

    1.1 dialer拨号 拨号盘点击拨号DialpadFragment的onClick方法会被调用 public void onClick(View view) { int resId = view. ...

  7. Android 4.4KitKat AudioRecord 流程分析

    Android是架构分为三层: 底层      Linux Kernel 中间层  主要由C++实现 (Android 60%源码都是C++实现) 应用层  主要由JAVA开发的应用程序 应用程序执行 ...

  8. HDFS2.x之RPC流程分析

    HDFS2.x之RPC流程分析 1 概述 Hadoop提供了一个统一的RPC机制来处理client-namenode, namenode-dataname,client-dataname之间的通信.R ...

  9. hadoop运行流程分析源代码级

    前言: 最近一直在分析hadoop的运行流程,我们查阅了大量的资料,虽然从感性上对这个流程有了一个认识但是我总是感觉对mapreduce的运行还是没有一个全面的认识,所以决定从源代码级别对mapred ...

随机推荐

  1. 与众不同 windows phone (40) - 8.0 媒体: 音乐中心的新增功能, 图片中心的新增功能, 后台音乐播放的新增功能

    [源码下载] 与众不同 windows phone (40) - 8.0 媒体: 音乐中心的新增功能, 图片中心的新增功能, 后台音乐播放的新增功能 作者:webabcd 介绍与众不同 windows ...

  2. php中的常用数组函数(三)(获取数组交集的函数们 array_intersect()、array_intersect_key()、array_intersect_assoc()、array_intersect_uassoc()、array_intersect_ukey())

    这5个获取交集的函数 有 5个对应的获取差集的函数.我是链接. array_intersect($arr1, $arr2); //获得数组同键值的交集 array_intersect_key($arr ...

  3. ABAP->内表数据下载到CSV格式(原创转载请注明)

    需求:将alv上面的数据计算到内表中区,然后通过自定义按钮进行下载到csv格式中 附加:现在基本不用csv导出了,但是有些变态需求强行要求,也只好研究出来了,excel与txt导出很简单,那就不多说了 ...

  4. 自制html5塔防游戏

    这是一款由html5里的canvas和普通html元素结合的小游戏,游戏比较简单单一.主要是以建塔,防御为主.下面是游戏的一张截图: 这里是游戏的地址,直接去玩下吧:http://www.lovewe ...

  5. Iscroll应用文档

    Iscroll是一个非常不错的区域滑动插件. 不过它有个小小的不足,就是它的说明文档. 全英文不说,整理的也不咋好,官网上看着很乱,不容易查阅. 因此上网找了一些相关的文档说明并加以整理. Iscro ...

  6. XML的解析方式(Java)

    dom方式解析 根据XML的层级结构在内存中分配一个树形结构,把XML的标签.属性和文本都封装成对象 优点:如果很方便实现增删改操作 缺点:如果文件过大,会造成内存溢出   sax方式解析 采用事件驱 ...

  7. 自定义SharePoint 2013 元数据选择控件

    元数据在Sharepoint中是一个很常用的功能,他提供一个方法来管理企业中常用的关键词,可以更加统一的使用和更新.默认情况下,绑定在列表或库中的元数据字段可以设置是否允许为多个值.但是无法控制在弹出 ...

  8. 基于MATLAB实现的云模型计算隶属度

    ”云”或者’云滴‘是云模型的基本单元,所谓云是指在其论域上的一个分布,可以用联合概率的形式(x, u)来表示 云模型用三个数据来表示其特征 期望:云滴在论域空间分布的期望,一般用符号Εx表示. 熵:不 ...

  9. English Training Material - 04

    Inviting What kinds of social activities in your city could be appropriate ways of entertaining visi ...

  10. iOS开发之NSTimer使用初探

    创建一个定时器(NSTimer) - (void)viewDidLoad { [super viewDidLoad]; [NSTimer scheduledTimerWithTimeInterval: ...