我想分叉一个go进程并获取新进程的id,但我在exec
或os
库中看到的只是启动一个新进程。 / p>
答案 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