写点什么

自己动手写 Docker 系列 -- 3.3 使用命令管道优化参数传递

作者:
  • 2022 年 3 月 20 日
  • 本文字数:2161 字

    阅读完需:约 7 分钟

简介

在上几篇中,基本是都是通过函数参数传递的方式进行的参数传递,本篇中使用 Linux 的管道优化参数传递

源码说明

同时放到了 Gitee 和 Github 上,都可进行获取



本章节对应的版本标签是:3.3,防止后面代码过多,不好查看,可切换到标签版本进行查看


在上几篇中,都是通过函数传递的方式获取的参数,但在书中说:用户输入的参数过长,或者其中带有一些特殊字符,那参数的获取就会有些问题


本篇中使用管道进行优化,这个管道有点类似于 go 中的 channel 和 Java 中的阻塞队列,可以写入和读取


当写满时,写进程就会阻塞,直到有读进程把管道的内容读出来


但读取时,如果管道为空,同样会被阻塞,一直等到有写进程忘里面写数据

编码实现

代码没有前面的多了,比较少了,主要为:


  • 管道的创建

  • 参数数据的写入

  • 参数数据的读取

1.管道的创建

首先是在 fork 进程启动的时候,创建出管道。写管道返回,用于写入数据;读管道作为文件句柄,传入新进程中


func NewParentProcess(tty bool) (*exec.Cmd, *os.File) {  // 生成管道    readPipe, writePipe, err := os.Pipe()    if err != nil {        log.Errorf("create pipe error: %v", err)        return nil, nil    }
cmd := exec.Command("/proc/self/exe", "init") cmd.SysProcAttr = &syscall.SysProcAttr{ Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWNET | syscall.CLONE_NEWIPC, } if tty { cmd.Stdin = os.Stdin cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr }
// 将管道的一端传入fork的进程中 cmd.ExtraFiles = []*os.File{readPipe} return cmd, writePipe}
复制代码

2.参数数据的写入

在 fork 进程起来后,我们将参数数据写入管道


func Run(tty bool, cmdArray []string, config *subsystem.ResourceConfig) {  parent, writePipe := container.NewParentProcess(tty)  if err := parent.Start(); err != nil {    log.Error(err)    return  }
...... sendInitCommand(cmdArray, writePipe)
log.Infof("parent process run") _ = parent.Wait() os.Exit(-1)}
// 将运行参数写入管道func sendInitCommand(array []string, writePipe *os.File) { command := strings.Join(array, " ") log.Infof("all command is : %s", command) if _, err := writePipe.WriteString(command); err != nil { log.Errorf("write pipe write string err: %v", err) return } if err := writePipe.Close(); err != nil { log.Errorf("write pipe close err: %v", err) }}
复制代码

3.参数数据的读取

没有读到数据时,会一直阻塞住,直到读取到了相关的参数数据


func RunContainerInitProcess() error {  ......
cmdArray := readUserCommand() path, err := exec.LookPath(cmdArray[0]) if err != nil { log.Errorf("can't find exec path: %s %v", cmdArray[0], err) return err } log.Infof("find path: %s", path) if err := syscall.Exec(path, cmdArray, os.Environ()); err != nil { log.Errorf("syscall exec err: %v", err.Error()) } return nil}
// 读取程序传入参数func readUserCommand() []string { // 进程默认三个管道,从fork那边传过来的就是第四个(从0开始计数) readPipe := os.NewFile(uintptr(3), "pipe") msg, err := ioutil.ReadAll(readPipe) if err != nil { log.Errorf("read init argv pipe err: %v", err) return nil } return strings.Split(string(msg), " ")}
复制代码

运行验证

运行的结果如下,符合预期:


➜  dockerDemo git:(main) ✗ ./main run -ti -mem 100m ls -l{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-16T06:11:56+08:00"}{"level":"info","msg":"memory cgroup path: /sys/fs/cgroup/memory/mydocker-cgroup","time":"2022-03-16T06:11:56+08:00"}{"level":"info","msg":"all command is : ls -l","time":"2022-03-16T06:11:56+08:00"}{"level":"info","msg":"parent process run","time":"2022-03-16T06:11:56+08:00"}{"level":"info","msg":"init come on","time":"2022-03-16T06:11:56+08:00"}{"level":"info","msg":"find path: /usr/bin/ls","time":"2022-03-16T06:11:56+08:00"}total 4672drwxr-xr-x 2 root root    4096 Mar 16 06:11 docsdrwxr-xr-x 3 root root    4096 Mar 15 08:33 example-rw-r--r-- 1 root root     382 Mar 15 08:33 go.mod-rw-r--r-- 1 root root    1965 Mar 15 08:33 go.sum-rw-r--r-- 1 root root   11558 Mar 15 08:33 LICENSE-rwxr-xr-x 1 root root 4746416 Mar 15 09:00 maindrwxr-xr-x 6 root root    4096 Mar 15 08:33 mydocker-rw-r--r-- 1 root root     473 Mar 15 08:33 README.md
复制代码

总结

本篇中使用管道优化了参数的传递,实现了目标


但在上篇中的加入的资源限制功能好像有点小缺陷,目前必须要传入资源限制参数,变成了一个必选的,后面把它优化下

发布于: 刚刚阅读数: 2
用户头像

关注

还未添加个人签名 2018.09.09 加入

代码是门手艺活,也是门艺术活

评论

发布
暂无评论
自己动手写Docker系列 -- 3.3使用命令管道优化参数传递_Docker_萧_InfoQ写作平台