其他goroutine恐慌不停止儿童进程 [英] Panic in other goroutine not stopping child process

查看:144
本文介绍了其他goroutine恐慌不停止儿童进程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要运行一个长时间运行的子进程,如果我退出父进程(出于任何原因),就会将其终止。



以下是代码:

  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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆