其他goroutine恐慌不停止儿童进程 [英] Panic in other goroutine not stopping child process
问题描述
我需要运行一个长时间运行的子进程,如果我退出父进程(出于任何原因),就会将其终止。
以下是代码:
cmd:= exec.Command (./long-process)
推迟cmd.Process.Kill()
if err!= nil {
log.Fatal(err)
var fail io.ReadCloser
fail.Close()
失败
在这里产生明显的
panic:runtime错误:无效的内存地址或无指针解引用
它按预期工作 - 子进程被终止。 / p>
但是这发生在goroutine中:
cmd:= exec。 Command(./ long-process)
推迟cmd.Process.Kill()
如果err!= nil {
log.Fatal(err)
$ func(){
var fail io.ReadCloser
fail.Close()
}()
恐慌仍然会发生,但似乎 defer
未被调用,子进程不会被杀死。
有什么办法可以解决这个问题?
更新我需要一个跨平台解决方案(至少对于Linux和FreeBSD)
最小的例子:
$ infinite-loop.sh
#!/ bin / bash
while true;做
睡觉1
完成
别忘了
chmod + x infinite-loop.sh
test1.go
(为简洁起见,省略错误检查):
包主要
导入(
时间
io
os / exec
运行时
func main(){
cmd:= exec.Command(./ infinite-loop.sh)
cmd .Start()
defer cmd.Process.Kill()
$ b $ func(){
time.Sleep(100 * time.Millisecond)
var fail io.ReadCloser
fail.Close()
}()
for {
runtime.Gosched()
}
}
运行
ps aux | grep infinite-loop.sh | grep -v grep | wc -l; \
去运行test1.go; \
ps aux | grep infinite-loop.sh | grep -v grep | wc -l
0 <--- !!
panic:运行时错误:无效内存地址或零指针解除引用
[信号0xb代码= 0x1地址= 0x20 pc = 0x2130]
goroutine 5 [running]:
main.main.func1()
... / multiline / test1.go:19 + 0x30
由main.main创建
... / multiline / test1.go:20 + 0x9a
goroutine 1 [runnable]:
runtime.Gosched()
/usr/local/Cellar/go/1.5.1/libexec /src/runtime/proc.go:166 + 0x14
main.main()
... / multiline / test1.go:23 + 0x9f
退出状态2
1 <--- !!
0退出前的过程0和退出后的1。
如果你注释掉goroutine代码 - 它可以正常工作。
现在我们可以杀死它:
kill $(ps aux | grep infinite-loop.sh | grep -v grep | awk {'print $ 2'})
没有自动杀死子进程的跨平台解决方案。
在Linux上,您可以使用 pdeathsig
功能:
cmd:= exec.Command(./ long-process)
cmd.SysProcAttr =& syscall.SysProcAttr {
Pdeathsig :syscall.SIGTERM,
}
在其他平台上,孩子需要确定何时自行退出。一种方法是监视父母给予的管道或套接字FD。您也可以让某个进程管理器监视进程并在出现问题时进行清理。
一般来说,恐慌应该是罕见的,并得到修复。如果您确实有一些容易引起恐慌的代码区域,您可以在本地恢复并在退出之前调用子进程清理。
I need to run a long-running child process and kill it if I quit (for any reason) out of parent application.
Here is the code:
cmd := exec.Command("./long-process")
defer cmd.Process.Kill()
if err != nil {
log.Fatal(err)
}
var fail io.ReadCloser
fail.Close()
The fail
here produces obvious
panic: runtime error: invalid memory address or nil pointer dereference
It works as expected - the child process is killed.
But this happens in a goroutine:
cmd := exec.Command("./long-process")
defer cmd.Process.Kill()
if err != nil {
log.Fatal(err)
}
go func() {
var fail io.ReadCloser
fail.Close()
}()
The panic still happens, but then it seems defer
is not called and the child process is not killed.
Any way to go around this?
UPDATE I need a cross-platform solution (at least for Linux and FreeBSD)
Minimal example:
infinite-loop.sh
#!/bin/bash
while true; do
sleep 1
done
Don't forget to
chmod +x infinite-loop.sh
test1.go
(error checking left out for brevity):
package main
import (
"time"
"io"
"os/exec"
"runtime"
)
func main() {
cmd := exec.Command("./infinite-loop.sh")
cmd.Start()
defer cmd.Process.Kill()
go func() {
time.Sleep(100 * time.Millisecond)
var fail io.ReadCloser
fail.Close()
}()
for {
runtime.Gosched()
}
}
Let's run
ps aux | grep infinite-loop.sh | grep -v grep | wc -l; \
go run test1.go; \
ps aux | grep infinite-loop.sh | grep -v grep | wc -l
0 <--- !!
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x20 pc=0x2130]
goroutine 5 [running]:
main.main.func1()
.../multiline/test1.go:19 +0x30
created by main.main
.../multiline/test1.go:20 +0x9a
goroutine 1 [runnable]:
runtime.Gosched()
/usr/local/Cellar/go/1.5.1/libexec/src/runtime/proc.go:166 +0x14
main.main()
.../multiline/test1.go:23 +0x9f
exit status 2
1 <--- !!
0 processes before and 1 after exit.
If you comment out goroutine code - it works fine.
Now we can kill it:
kill $(ps aux | grep infinite-loop.sh | grep -v grep | awk {'print $2'})
There's no cross-platform solution to automatically kill a child process.
On Linux, you can use the pdeathsig
functionality:
cmd := exec.Command("./long-process")
cmd.SysProcAttr = &syscall.SysProcAttr{
Pdeathsig: syscall.SIGTERM,
}
On other platforms, the child needs to determine when to exit on its own. One way is to monitor a pipe or socket FD given to it from the parent. You could also have a process manager of some sort monitor the processes and cleanup if something goes wrong.
In general though, panics should be rare and get fixed. If you do have areas of code that are prone to panic'ing, you can recover locally and call for the cleanup of child processes before exiting.
这篇关于其他goroutine恐慌不停止儿童进程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!