上一篇文章中,讲解了如何通过Go语言实现对Docker Cgroup的资源限制
具体文章可见《Docker就应该这么学-07》
有需要的小伙伴可以回顾一下。
接下来本文会详细介绍一下Docker 管道及环境变量识别
管道及环境变量识别
获取代码
git clone https://gitee.com/mjreams/docker.git
当在Linux上创建两个进程时,进程之间的通信一般就会使用管道的机制。所谓管道,就是一个连接两个进程的通道,它是Linux支持IPC的其中一种方式。一般来说,管道都是半双工的,一端进行写操作,另外一端进行读操作。
常用的管道分为两种类型。一种类型是无名管道,它一般用于具有亲缘关系的进程之间;另外一种是有名管道,或者叫FIFO管道,它是一种存在于文件系统的管道,可以被两个没有任何亲缘关系的进程进行访问。有名管道一般可以通过mkfifo()函数来创建。
从本质上来说,管道也是文件的一种,但是它和文件通信的区别在于,管道有一个固定大小的缓冲区,大小一般是4KB。当管道被写满时,写进程就会被阻塞,直到有读进程把管道的内容读出来。同样地,当读进程从管道内拿数据的时候,如果这时管道的内容是空的,那么读进程同样会被阻塞,一直等到有写进程向管道内写数据。
首先增加了一个函数,使用Go提供的pipe方法生成一个匿名管道。这个函数返回两个变量,一个是读一个是写,其类型都是文件类型。
见container/container_process.go
这个方法的改动点己经表明,这里主要解决的问题是如何将创建的管道的一端传给子进程。这是一个文件类型,肯定不能通过字符参数的方式进行传递,因此使用了command的cmd.ExtraFiles方法。这个属性的意思是会外带着这个文件句柄去创建子进程。为什么叫“外带着”呢?因为1个进程默认会有3个文件描述符,分别是标准输入、标准输出、标准错误。这3个是子进程一创建的时候就会默认带着的,那么外带的这个文件描述符理所当然地就成为了第4个。
这里可以看到默认的文件描述符。通过这种方式,就把管道的一端传给子进程了。下面来看子进程的改动。
见container/init.go
这里得到父进程传递过来的管道一端后,就直接去读,由于此时父进程可能还没写入,所以此时的读操作就会停在这里等待输入。然后,看一下最终的init方法做了什么。
可以看到,init进程读取了父进程传递过来的参数后,在子进程内进行了执行,这样就完成了将用户指定命令传递给子进程的操作。
最后,来看一下父进程是在哪里发送的参数。
见run.go Run()
//发送用户命令
sendInitCommand(comArray, writePipe)
在父进程中,当进程start完毕后才发送参数,也就是说,子进程这时应该己经在读等待了。