背景
forkmaster-slaveforkgoroutinefork
fork + exec
fork
为了实现上述需求,我们需要利用Go语言的一些特性进行模拟。
C语言的实现方式
在了解Go如何实现fork之前,先看一下C里的传统使用方式:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
void child() {
printf("child process\n");
}
int main() {
printf("main process\n");
pid_t pid = fork();
int wstatus;
if (pid == 0) {
child();
} else {
printf("main exit\n");
waitpid(pid, &wstatus, 0);
}
}
运行一下:
$ gcc main.c && ./a.out
main process
main exit
child process
Go语言的实现方案
然后我们看看Docker提供的实现fork的方式:
package main
import (
"log"
"os"
"github.com/docker/docker/pkg/reexec"
)
func init() {
log.Printf("init start, os.Args = %+v\n", os.Args)
reexec.Register("childProcess", childProcess)
if reexec.Init() {
os.Exit(0)
}
}
func childProcess() {
log.Println("childProcess")
}
func main() {
log.Printf("main start, os.Args = %+v\n", os.Args)
cmd := reexec.Command("childProcess")
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Panicf("failed to run command: %s", err)
}
if err := cmd.Wait(); err != nil {
log.Panicf("failed to wait command: %s", err)
}
log.Println("main exit")
}
运行一下:
$ go run main.go
2018/03/08 19:52:39 init start, os.Args = [/tmp/go-build209640177/b001/exe/main]
2018/03/08 19:52:39 main start, os.Args = [/tmp/go-build209640177/b001/exe/main]
2018/03/08 19:52:39 init start, os.Args = [childProcess]
2018/03/08 19:52:39 childProcess
2018/03/08 19:52:39 main exit
fork
initmainos.Args[0]reexec.Commandos.Args[0]reexec.Initreexec.Registeros.Exit(0)fork
总结
runtimeinitmainforkfork