Golang中實現典型的fork呼叫
ofollow,noindex" target="_blank">English Version
https://github.com/moby/moby/tree/master/pkg/reexec
Golang中沒有提供fork
呼叫,只有
這三個都類似於fork + exec
,但是沒有類似C中的fork呼叫,在fork之後根據返回的pid
然後進入不同的函式。原因在:
https://stackoverflow.com/questions/28370646/how-do-i-fork-a-go-process/28371586#28371586
簡要翻譯一下:
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
我們看看Docker提供的實現的使用方式:
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
其原理是init
一定會在main
之前執行。而初次執行程式的時候os.Args[0]
是可執行檔案的名字。
但是reexec.Command
卻可以修改子程序的os.Args[0]
。所以子程序會直接找到reexec.Init
上reexec.Register
所註冊的函式,然後執行,返回true。然後呼叫os.Exit(0)