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