如何在Go代码超时时杀死进程及其子进程?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在Go代码超时时杀死进程及其子进程?相关的知识,希望对你有一定的参考价值。

我有一种情况,我需要一段时间后终止进程。我开始该过程,然后:

case <-time.After(timeout):
        if err := cmd.Process.Kill(); err != nil {
            return 0, fmt.Errorf("Failed to kill process: %v", err)
        }

杀死进程。但这只会杀死父进程,而不会杀死主进程启动的5-10个子进程。我也尝试创建一个进程组,然后执行:

syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)

杀死主进程和孙进程,但不起作用。还有其他方法可以终止进程。

答案

我认为这是您所需要的:

cmd := exec.Command(command, arguments...)

// This sets up a process group which we kill later.
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

if err := cmd.Start(); err != nil {
    return err
}

// buffered chan is important so the goroutine does't
// get blocked and stick around if the function returns
// after the timeout
done := make(chan error, 1)

go func() {
    done <- cmd.Wait()
}()

select {
case err := <-done:
    // this will be nil if no error
    return err
case <-time.After(time.Second):
    // We created a process group above which we kill here.
    pgid, err := syscall.Getpgid(cmd.Process.Pid)
    if err != nil {
        return err
    }
    // note the minus sign
    if err := syscall.Kill(-pgid, 15); err != nil {
        return err
    }
    return fmt.Errorf("Timeout")
}
另一答案

尚不清楚您是否可以控制这些子进程。如果是这样,您可以考虑使用以下Linux功能(也不要说它是否特定于操作系统)。

此代码行要求内核在父母去世时向孩子发送SIGHUP。这样,您的Go进程就可以杀死父级,并自动杀死所有子级。不仅如此,它永远不会失败!内核真的很不错。

prctl(PR_SET_PDEATHSIG, SIGHUP);

当然,如果您这样做,则存在竞争条件。也就是说,在孩子调用此prctl()函数时,父母可能已经死亡,在这种情况下,孩子需要立即退出。

if(getppid() != parent_pid)
{
    exit(1);
}

因此避免竞争条件的完整代码是:

// must happen before the fork() call
const pid_t parent_pid = getpid();

const pid_t child_pid = fork();

if(child_pid != 0)
{
    // fork() failed (child_pid == -1) or worked (an actual PID)
    ...
    return;
}

prctl(PR_SET_PDEATHSIG, SIGHUP);

if(getppid() != parent_pid)
{
    exit(1);
}

注意:在这种情况下,习惯上使用SIGHUP。您可能还需要考虑其他信号,尤其是在子代处理管道/套接字的情况下(在这种情况下,您可能会忽略SIGHUP!)或出于其他原因而需要处理SIGHUP

现在,如果您对子进程的代码没有任何控制...,您可以尝试通过搜索所有子进程,一一杀死它们,然后杀死父进程,从Go应用程序中杀死每个进程。但是,除非您无法阻止整个孩子树创建新流程,否则您总是会遇到无法避免的竞争条件。如果可以的话,只需注册所有这些孩子的PID并逐个杀死它们。

当然,如果可以创建一个组,那就更好了。就像上面的SIGHUP一样,杀死组的所有成员都是由内核完成的,并且不会丢失任何进程。

以上是关于如何在Go代码超时时杀死进程及其子进程?的主要内容,如果未能解决你的问题,请参考以下文章

通过其子进程分析一个进程并在之后杀死该子进程

杀死父进程后将标准输入传递给子进程

如何在linux中使用shell脚本杀死进程时获取退出代码=0

如何通过 VBA 代码杀死任务管理器进程?

如何杀死一个知道pid的进程

pkill有的时候并不能杀死进程?