简介
在上几篇中,基本是都是通过函数参数传递的方式进行的参数传递,本篇中使用 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
   复制代码
 总结
本篇中使用管道优化了参数的传递,实现了目标
但在上篇中的加入的资源限制功能好像有点小缺陷,目前必须要传入资源限制参数,变成了一个必选的,后面把它优化下
评论