为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

Go
守候你守候我 2021-11-08 19:29:34
我有一种情况,我需要在一段时间后终止一个进程。我开始这个过程,然后: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)杀死主进程和孙进程,但不起作用。有没有其他方法可以终止进程。
查看完整描述

2 回答

?
胡说叔叔

TA贡献1804条经验 获得超8个赞

我认为这就是你需要的:


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")

}


查看完整回答
反对 回复 2021-11-08
?
慕容3067478

TA贡献1773条经验 获得超3个赞

目前尚不清楚您是否可以控制这些子进程。如果是这样,您可以考虑使用以下 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 一样,杀死一个组的所有成员是由内核完成的,它不会错过任何进程。


查看完整回答
反对 回复 2021-11-08
  • 2 回答
  • 0 关注
  • 362 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信