How to implement fork syscall in Golang?
ref: https://github.com/moby/moby/tree/master/pkg/reexec
We don’t have a fork
syscall in Golang, we have:
All those three functions is like a combination of fork + exec
, but there has no pure fork
syscall just like in
C programming language(after syscall, which will return pid in caller). Reasons can be found in here:
It mainly says:
fork()
has been invented at the time when no threads were used at all, and a process had always had just a single thread of execution in it, and hence forking it was safe.- In C, you control all the threads by your hand, but in Go, you cannot, so threads will be out of control after call
fork
withoutexec
, so Go providesfork + exec
only.
Let’s have a look at how we use pure fork
in 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);
}
}
Run it:
$ gcc main.c && ./a.out
main process
main exit
child process
Let’s look how can we implements this in Go:
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")
}
Run it:
$ 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
Explanation:
init
will be execute before main
function. When you execute the binary executable file from command line,
os.Args[0]
will be the name of binary executable file, but, reexec.Command
will change os.Args[0]
, so
child process will find function registed by reexec.Register
, and execute it, return true
, then call os.Exit(0)
.
更多文章
- socks5 协议详解
- zerotier简明教程
- 搞定面试中的系统设计题
- frp 源码阅读与分析(一):流程和概念
- 用peewee代替SQLAlchemy
- Golang(Go语言)中实现典型的fork调用
- DNSCrypt简明教程
- 一个Gunicorn worker数量引发的血案
- Golang validator使用教程
- Docker组件介绍(二):shim, docker-init和docker-proxy
- Docker组件介绍(一):runc和containerd
- 使用Go语言实现一个异步任务框架
- 协程(coroutine)简介 - 什么是协程?
- SQLAlchemy简明教程
- Go Module 简明教程