我如何 fork 一个 go 进程?

How do I fork a go process?

我想分叉一个 go 进程并取回新进程的 ID,但我在 execos 库中只能看到启动一个新进程.

一种解决方案是使用 exec.Command 在其 goroutine 中执行。

这就是小项目 akshaydeo/go_process 所做的:

// Method to fork a process for given command
// and return ProcessMonitor
func Fork(processStateListener ProcessStateListener, cmdName string, cmdArgs ...string) {
    go func() {
        processMonitor := &ProcessMonitor{}
        args := strings.Join(cmdArgs, ",")
        command := exec.Command(cmdName, args)
        output, err := command.Output()
        if err != nil {
            processMonitor.Err = err
            processStateListener.OnError(processMonitor, err)
        }       
        processMonitor.Output = &output
        processStateListener.OnComplete(processMonitor)
    }()
}

The test process_test.go 显示一些示例:

// Test case for fork
func TestFork(t *testing.T) {
    processStateListenerImpl := &ProcessStateListenerImpl{make(chan bool)}
    Fork(processStateListenerImpl,"ls", "-a") //("ping","192.168.3.141","-c","3")
    // waiting onto monitor
    <-processStateListenerImpl.monitor
}

您应该想要 syscall 包中的 syscall.ForkExec()

请注意,fork() 是在根本没有使用线程的时候发明的,一个进程中始终只有一个执行线程,因此分叉它是安全的。对于 Go,情况完全不同,因为它大量使用 OS 级线程来为其 goroutine 调度提供动力。

现在,Linux 上的朴素 fork(2) 将使子进程在所有活动线程中只有一个线程——在父进程中调用 fork(2) 的线程,包括 Go 运行时使用的一些关键线程。基本上这意味着你 不能指望子进程能够继续执行 Go 代码, 并且你唯一可以明智地做的就是以某种方式立即执行 exec(2)。请注意,这就是 syscall.ForkExec() 的用途。

现在进一步思考这个问题。我想说这些天直接调用 fork(2) 唯一有用的是 "best-effort asynchronous process state snapshotting" — 比如说 Redis 使用的那种。该技术依赖于子进程从其父进程继承所有内存数据页这一事实,但是 OS 使用写时复制技术并没有真正复制所有数据,因此子进程可以坐在那里保存将所有数据结构写入磁盘,而其父级正在修改它们自己的地址 space。 fork() 的所有其他可能的用途都意味着立即 exec(),这就是 exec.Command() 等人的用途,那么为什么不使用它呢?

syscall.Syscall(syscall.SYS_FORK, 0, 0, 0) 可能有效,第一个 return 值是你想要的 id。

这是一个例子:

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)
    }
}

然后得到类似这样的输出:

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