通过前面的准备我们可以开始写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不变。
以上就是这次的内容,谢谢阅读。