1、 runc/utils_linux.go
func (r *runner) run(config *specs.Process) (int , error)
在该函数中第一次对容器的IO进行了处理,首先调用tty, err := setupIO(process, rootuid, rootgid, r.console, config.Terminal, r.detach, || r.create),创建并返回了一个结构tty,tty的具体类型如下所示:
type tty struct { console libcontainer.Console state *term.State closers []io.Closer postStart []io.Closer wg sync.WaitGroup }
之后又有handler := newSignalHandler(tty, r.enableSubreaper)以及tty.ClosePostStart(), status, err := handler.forward(process)对tty进行处理(主要是针对winsize)。同时,我们可以发现在handler.forward函数之前有对r.detach进行判断,若r.detach为true则直接返回了。
2、runc/utils_linux.go
// setupIO sets the proper IO on the process depending on the configuration
// detach and createTTy will not work unless a console path is passed --->即detach 和createTty不能同时为true,除非console不为空
(1)当createTTy为true时,调用 return createTty(process, rootuid, rootgid, console) ---> 要求tty,则优先创建tty
(2)当detach为true时,调用dupStdio(process, rootuid, rootgid), 最后再返回一个空的tty, return &tty{}
(3)当createTTy和detach都为false时,则return createStdioPipes(process, rootuid, rootgid)
------------------------------------------------------------ tty方式 ----------------------------------------------------
3、runc/tty.go
func createTty(p *libcontainer.Process, rootuid, rootgid int, consolePath string) (*tty, error)
(1)当用户指定了consolePath的时候,则直接调用p.consoleFromPath(consolePath),再返回一个空的tty, return &tty{}
(2)当用户未指定consolePath时,则调用console, err := p.NewConsole(rootuid, rootgid)创建一个新的console,并且将标准输入输出与console相连。接着调用state, err := term.SetRawTerminal(os.Stdin.Fd())函数将标准输入设置为raw mode。最后返回:
return &tty { console: console, state: state, closers: []io.Closer{ console, }, }
4、runc/libcontainer/console_linux.go
// newConsoleFromPath is an internal function returning an initialized console for use inside a container‘s MNT namespace
func newConsoleFromPath(slavePath string) *linuxConsole
这个函数只是简单地返回, return &linuxConsole{slavePath, slavePath}, linuxConsole的定义如下所示:
type linuxConsole struct { master *os.File slavePath string }
5、runc/libcontainer/console_linux.go
// NewConsole returns an initialized console that can be used within a container by copying bytes
// from the master side to the slave that is attached as the tty for the container‘s init process
func NewConsole(uid, gid int) (Console, error)
首先调用master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR | syscall.O_NOCTTY|syscall.O_CLOEXEC, 0),打开主设备,接着调用console, err := ptsname(master)获取从设备的设备名,之后再对主从设备进行一系列的操作。最后,return &linuxConsole{ slavePath: console, master: master}
------------------------------------------------------------------------ detach 模式---------------------------------------------------------------
6、runc/utils_linux.go
func dupStdio(process *libcontainer.Process, rootuid, rootgid int) error
该函数的操作很简单,就是直接将标准IO,os.Stdin, os.Stdout, os.Stderr和process的Stdin, Stdout, Stderr相连,然后改变这几个文件描述符的uid和gid即可。
--------------------------------------------------------------------------- 不分配terminal,不detach ------------------------------------------
7、runc/tty.go
// setup standard pipes so that the TTY of the calling runc process is not inherited by the container
func createStdioPipes(p *libcontainer.Process, rootuid, rootuid int) (*tty, error)
首先调用i, err := p.InitializeIO(rootuid, rootgid),其中函数InitializeIO函数做的工作其实就是创建了三个管道,并将一端连接process的STDIO,再将另一端返回给i。接着对tty 结构进行填充,主要将STDIO赋值给tty.Closers和tty.postStart。最后将i和os.StdIO相连。