如何在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代码超时时杀死进程及其子进程?的主要内容,如果未能解决你的问题,请参考以下文章