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")
}
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 一样,杀死一个组的所有成员是由内核完成的,它不会错过任何进程。
- 2 回答
- 0 关注
- 362 浏览
添加回答
举报