假设我有一个进程,它只生成一个子进程。现在,当父进程由于某种原因(正常或不正常,通过kill, ^C,断言失败或其他任何原因)退出时,我希望子进程死亡。如何正确地做到这一点?


关于stackoverflow的一些类似问题:

(前面问过)当父进程退出的时候,我怎么能让子进程退出呢? (稍后问)用fork()创建的子进程会在父进程被杀死时自动被杀死吗?


关于Windows的stackoverflow也有类似的问题:

如何在Windows中自动销毁子进程? 当父进程被杀死时,杀死子进程


当前回答

正如其他人指出的那样,当父进程退出时,依赖父进程pid变成1是不可移植的。不需要等待特定的父进程ID,只需要等待ID发生变化:

pit_t pid = getpid();
switch (fork())
{
    case -1:
    {
        abort(); /* or whatever... */
    }
    default:
    {
        /* parent */
        exit(0);
    }
    case 0:
    {
        /* child */
        /* ... */
    }
}

/* Wait for parent to exit */
while (getppid() != pid)
    ;

如果不想以全速轮询,可以根据需要添加微睡眠。

在我看来,这个选项比使用管道或依赖于信号更简单。

其他回答

如果你发送一个信号到pid 0,使用for实例

kill(0, 2); /* SIGINT */

该信号被发送到整个进程组,从而有效地杀死了子进程。

你可以很容易地测试它,比如:

(cat && kill 0) | python

如果你然后按^D,你会看到文本“Terminated”,这表明Python解释器确实已经被杀死,而不是因为stdin被关闭而退出。

通过在prctl()系统调用中指定PR_SET_PDEATHSIG选项,子进程可以要求内核在父进程死亡时传递SIGHUP(或其他信号),就像这样:

prctl(PR_SET_PDEATHSIG, SIGHUP);

详见man 2 prctl。

编辑:这是linux专用的

如果父母去世,孤儿的PPID变为1 -你只需要检查你自己的PPID。 在某种程度上,这就是上面提到的轮询。 这是它的外壳部分:

check_parent () {
      parent=`ps -f|awk '$2=='$PID'{print $3 }'`
      echo "parent:$parent"
      let parent=$parent+0
      if [[ $parent -eq 1 ]]; then
        echo "parent is dead, exiting"
        exit;
      fi
}


PID=$$
cnt=0
while [[ 1 = 1 ]]; do
  check_parent
  ... something
done

我在过去通过在“子”中运行“原始”代码和在“父”中运行“衍生”代码来实现这一点(也就是说:你在fork()之后反转了通常意义上的测试)。然后在“衍生”代码中捕获SIGCHLD…

对你来说可能不行,但管用的时候很可爱。

受到这里另一个答案的启发,我提出了以下全posix解决方案。一般思想是在父进程和子进程之间创建一个中间进程,其目的只有一个:注意父进程何时死亡,并显式地终止子进程。

当子进程中的代码无法修改时,这种解决方案非常有用。

int p[2];
pipe(p);
pid_t child = fork();
if (child == 0) {
    close(p[1]); // close write end of pipe
    setpgid(0, 0); // prevent ^C in parent from stopping this process
    child = fork();
    if (child == 0) {
        close(p[0]); // close read end of pipe (don't need it here)
        exec(...child process here...);
        exit(1);
    }
    read(p[0], 1); // returns when parent exits for any reason
    kill(child, 9);
    exit(1);
}

使用这种方法有两个小注意事项:

如果你故意杀死中间进程,那么当父进程死亡时,子进程不会被杀死。 如果子进程在父进程之前退出,那么中间进程将尝试杀死原来的子进程pid,该进程现在可以引用一个不同的进程。(这可以通过在中间过程中编写更多代码来解决。)

顺便说一句,我使用的实际代码是Python的。为了完整起见,这里是:

def run(*args):
    (r, w) = os.pipe()
    child = os.fork()
    if child == 0:
        os.close(w)
        os.setpgid(0, 0)
        child = os.fork()
        if child == 0:
            os.close(r)
            os.execl(args[0], *args)
            os._exit(1)
        os.read(r, 1)
        os.kill(child, 9)
        os._exit(1)
    os.close(r)