在Go代码中终止超时的进程

时间:2015-10-03 11:03:05

标签: go process

我有一些情况需要在一段时间后杀死进程。我开始这个过程然后

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 个答案:

答案 0 :(得分:2)

我认为这就是你所需要的:

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

答案 1 :(得分:0)

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

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

def check_auth(id_token):
    decoded_token = auth.verify_id_token(id_token)
    uid = decoded_token['uid']
    print(uid)
    return uid


def requires_auth(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        auth = request.headers.get("Authorization")
        if not auth or not check_auth(auth):
            message = {"error": "Authorization Required"}
            resp = message
            return resp
        return f(*args, **kwargs)

    return decorated

当然,如果您这样做的话,就有竞争条件。也就是说,当孩子调用此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

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

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