3 回答
TA贡献1859条经验 获得超6个赞
你应该想syscall.ForkExec()从syscall包。
请注意,fork()它是在根本不使用线程的时候发明的,并且一个进程中始终只有一个执行线程,因此分叉它是安全的。对于 Go,情况完全不同,因为它大量使用操作系统级线程来为其 goroutine 调度提供动力。
现在,fork(2)在 Linux 上未经修饰将使子进程只有一个线程——fork(2)在父进程中调用的那个——在所有活动的线程中,包括 Go 运行时使用的一些关键线程。基本上这意味着您根本不能期望子进程能够继续执行 Go 代码,并且您唯一可以明智地做的就是以某种方式立即执行exec(2). 请注意,这syscall.ForkExec()就是应该用于的内容。
现在进一步考虑这个问题。我想说现在直接调用唯一fork(2)有用的东西是“尽力而为的异步进程状态快照”——比如Redis使用的那种。这种技术依赖于子进程从其父进程继承所有内存数据页的事实,但操作系统使用写时复制技术并没有真正复制所有数据,因此子进程可以坐在那里保存所有数据结构到磁盘,而其父级正在自己的地址空间中修改它们。其他所有可以想到的 for 用法都fork()意味着直接exec(),这就是exec.Command()et al 的用途,那么为什么不使用它呢?
TA贡献1812条经验 获得超5个赞
一种解决方案是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)
}()
}
该测试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
}
TA贡献1772条经验 获得超5个赞
分叉不仅仅用于并行处理。它还用于为无法关闭的关键服务提供冗余。如果您只有一个进程,则致命错误可能会导致整个事物、线程和所有事物消失。
对于多进程应用程序,应用程序的另一个并行实例将继续处理,而“父”进程会产生一个新实例来替换崩溃的实例。
Nginx、Apache 都以这种方式工作。
- 3 回答
- 0 关注
- 310 浏览
添加回答
举报
