用go写一个docker(9)-初步构造容器

通过前面的准备我们可以开始写docker了。一口吃不出一个胖子,我们慢慢吃,到最后不会吃成胖子,但能把东西吃完也是极好的。所以我们先实现一个run版的docker,后续再慢慢加其他功能。

本次的完整代码可参考:

https://github.com/xianlubird/mydocker/tree/code-3.2

为了便于理解,我精简了一下:

https://github.com/warshipJian/jiandocker/tree/code-1

主要看command.go文件:

var runCommand = cli.Command{
  Name: "run",
  Usage: `run a basic docker
      jiandocker run [command] -m [limit memory]`,
  Flags: []cli.Flag{
    cli.StringFlag{
      Name: "m",
      Usage: "memory limit",
    },
  },
  Action: func(context *cli.Context) error {
    if len(context.Args()) < 1 {
      return fmt.Errorf("Missing container command")
    }
    command := context.Args().Get(0)
    args := []string{"init",command}
    cmd := exec.Command("/proc/self/exe", args...)
    cmd.SysProcAttr = &syscall.SysProcAttr{
      Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS |
        syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC,
    }
    cmd.Stdin = os.Stdin
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    if err := cmd.Start(); err != nil {
      log.Error(err)
    }
    // limit memory
    limitMemory := context.String("m")
    if limitMemory != "" {
      MemoryLimit(cmd.Process.Pid,limitMemory)
    }
    cmd.Wait()
    return nil
  },
}
​
var initCommand = cli.Command{
  Name: "init",
  Usage: `mount proc system `,
  Action: func(context *cli.Context) error {
    // const mount namespace
    syscall.Mount("","/","", syscall.MS_PRIVATE | syscall.MS_REC, "")
    defaultMountFlags := syscall.MS_NOEXEC | syscall.MS_NOSUID | syscall.MS_NODEV
    _ = syscall.Mount("proc", "/proc", "proc", uintptr(defaultMountFlags), "")
    command := context.Args().Get(0)
    argv := []string{command}
    if err := syscall.Exec(command, argv, os.Environ()); err != nil {
      log.Errorf(err.Error())
    }
    return nil
  },
}

定义了两个命令:runCommand 和 initCommand,步骤如下:

在终端运行sudo ./jiandocker run /bin/bash -m 20m 

执行runCommand部分的代码
在runCommand中通过/proc/self/exe init调用initCommand

在initCommand中挂载proc,通过syscall.Exec覆盖进程之前的程序背景,从而实现docker run /bin/bash的效果。

这里的关键点是/proc/self/exe和syscall.Exec。

/proc/self/exe

/proc/self/exe相当于调用自己,之前简单介绍过一次。
command.go文件的17行如下:

exec.Command("/proc/self/exe", args...)

这里args的内容为:

["init","/bin/bash"]

执行命令sudo ./jiandocker run /bin/bash -m 20m到这行时,相当于执行:

sudo ./jiandocker init /bin/bash

syscall.Exec

linux中exec实现的效果是:以新的进程去代替原来的进程,但进程的PID保持不变。exec系统调用并没有创建新的进程,只是替换了原来进程上下文的内容。原进程的代码段,数据段,堆栈段被新的进程所代替。
常用组合:fork之后调用exec修改程序背景,进程pid不变。

以上就是这次的内容,谢谢阅读。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值