简介
在上几篇中,基本是都是通过函数参数传递的方式进行的参数传递,本篇中使用 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 4672
drwxr-xr-x 2 root root 4096 Mar 16 06:11 docs
drwxr-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 main
drwxr-xr-x 6 root root 4096 Mar 15 08:33 mydocker
-rw-r--r-- 1 root root 473 Mar 15 08:33 README.md
复制代码
总结
本篇中使用管道优化了参数的传递,实现了目标
但在上篇中的加入的资源限制功能好像有点小缺陷,目前必须要传入资源限制参数,变成了一个必选的,后面把它优化下
评论