我如何分叉过程?

时间:2015-02-06 16:45:58

标签: go exec fork

我想分叉一个go进程并获取新进程的id,但我在execos库中看到的只是启动一个新进程。 / p>

4 个答案:

答案 0 :(得分:27)

您可能希望syscall包中的syscall.ForkExec()

注意fork()是在没有使用任何线程的时候发明的,并且一个进程总是只有一个执行线程,因此分支它是安全的。使用Go,情况完全不同,因为它大量使用操作系统级线程来支持其goroutine调度。

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

现在进一步思考这个问题。我说现在直接调用fork(2)唯一有用的是“尽力而为的异步进程状态快照” - 比如Redis使用的那种。这种技术依赖于子进程从其父进程继承所有内存数据页的事实,但操作系统使用写时复制技术并不真正复制所有数据,因此子进程可以坐在那里保存所有数据结构磁盘,而它的父进程在其自己的地址空间中修改它们。 fork()的所有其他可能的用途意味着立即exec(),这就是exec.Command() et al的用途,为什么不使用呢?

答案 1 :(得分:0)

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

这就是小项目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
}

答案 2 :(得分:0)

分叉不仅仅用于并行处理。它还用于为无法关闭的关键服务提供冗余。如果您只有一个进程,则致命错误可能会导致整个进程、线程和所有进程中断。

对于多进程应用程序,应用程序的其他并行实例将继续处理,而“父”进程会产生一个新实例来替换崩溃的实例。

Nginx、Apache 都是这样工作的。

答案 3 :(得分:0)

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("In child:", id, foo, bar)
    } else {
        bar++
        fmt.Println("In parent:", id, foo, bar)
    }
}

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

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