如何在Go中分叉(fork)一个进程?

huangapple go评论78阅读模式
英文:

How do I fork a go process?

问题

我想要分叉(fork)一个 Go 进程,并获取新进程的 ID,但是在 execos 库中,我只能看到启动一个新进程的方法。

英文:

I want to fork a go process and get back the id of the new process(es), but all I can see in the exec or os libraries is to start a new process.

答案1

得分: 55

你可能想要使用syscall包中的syscall.ForkExec()

请注意,fork()是在没有使用线程的时候发明的,一个进程始终只有一个执行线程,因此fork它是安全的。而在Go语言中,情况完全不同,它大量使用操作系统级别的线程来支持goroutine的调度。

在Linux上,不带任何修饰的fork(2)会使子进程只有一个线程,即在父进程中调用fork(2)的那个线程,而其他活动的线程,包括Go运行时使用的一些关键线程,都不会被复制到子进程中。基本上,这意味着你不能指望子进程能够继续执行Go代码,你唯一能明智地做的事情就是立即执行exec(2)。注意,这正是syscall.ForkExec()的用途。

现在进一步思考这个问题。我认为如今直接调用fork(2)只有一个用途,那就是“尽力异步进程状态快照”,比如Redis使用的那种情况。这种技术依赖于子进程继承其父进程的所有内存数据页的事实,但操作系统使用写时复制技术,并不真正复制所有数据,因此子进程可以在父进程在自己的地址空间中修改这些数据结构时,静静地将所有数据保存到磁盘上。fork()的其他任何用途都意味着立即执行exec(),而exec.Command()等函数就是为此而设计的,所以为什么不直接使用它呢?

英文:

You supposedly want syscall.ForkExec() from the syscall package.

Note that 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. With Go, the situation is radically different as it heavily uses OS-level threads to power its goroutine scheduling.

Now, unadorned fork(2) on Linux will make the child process have just the single thread—the one which called fork(2) in the parent process—among all those which were active, including some crucial threads used by the Go runtime. Basically this means that you simply cannot expect the child process to be able to continue executing Go code, and the only thing you can sensibly do is to somehow immediately perform exec(2). Notice that that's what syscall.ForkExec() is supposed to be used for.

And now think about the problem further. I'd say these days the only thing a direct call to fork(2) is useful for is "best-effort asynchronous process state snapshotting"—the kind, say, Redis uses. This technique relies on the fact the child process inherits all the memory data pages from its parent, but the OS uses copy-on-write technique to not really copy all that data, so the child can just sit there and save all the data structures to disk while its parent is chugging away modifying them in its own address space. Every other conceivable use for fork() implies immediate exec(), and that's what exec.Command() et al is for, so why just not use it?

答案2

得分: 2

syscall.Syscall(syscall.SYS_FORK, 0, 0, 0) 可能会起作用,第一个返回值是你想要的id。

以下是一个示例:

func main() {
	foo := 4
	bar := 10
	id, _, _ := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
	if id == 0 {
		foo++
		fmt.Println("在子进程中:", id, foo, bar)
	} else {
		bar++
		fmt.Println("在父进程中:", id, foo, bar)
	}
}

然后得到类似于以下输出:

在父进程中: 16397 4 11
在子进程中: 0 5 10
英文:

syscall.Syscall(syscall.SYS_FORK, 0, 0, 0) might work, and first return value is id you want.

Here's an example:

func main() {
	foo := 4
	bar := 10
	id, _, _ := syscall.Syscall(syscall.SYS_FORK, 0, 0, 0)
	if id == 0 {
		foo++
		fmt.Println("In child:", id, foo, bar)
	} else {
		bar++
		fmt.Println("In parent:", id, foo, bar)
	}
}

then get output similar to this:

In parent: 16397 4 11
In child: 0 5 10

huangapple
  • 本文由 发表于 2015年2月7日 00:45:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/28370646.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定