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


关于stackoverflow的一些类似问题:

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


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

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


当前回答

即使7年过去了,我刚刚遇到这个问题,因为我正在运行SpringBoot应用程序,需要在开发期间启动webpack-dev-server,并需要在后端进程停止时杀死它。

我尝试使用Runtime.getRuntime()。addShutdownHook,但它在Windows 10上工作,但在Windows 7上不工作。

我已经将其更改为使用一个专门的线程来等待进程退出或用于InterruptedException,这似乎在两个Windows版本上都正确工作。

private void startWebpackDevServer() {
    String cmd = isWindows() ? "cmd /c gradlew webPackStart" : "gradlew webPackStart";
    logger.info("webpack dev-server " + cmd);

    Thread thread = new Thread(() -> {

        ProcessBuilder pb = new ProcessBuilder(cmd.split(" "));
        pb.redirectOutput(ProcessBuilder.Redirect.INHERIT);
        pb.redirectError(ProcessBuilder.Redirect.INHERIT);
        pb.directory(new File("."));

        Process process = null;
        try {
            // Start the node process
            process = pb.start();

            // Wait for the node process to quit (blocking)
            process.waitFor();

            // Ensure the node process is killed
            process.destroyForcibly();
            System.setProperty(WEBPACK_SERVER_PROPERTY, "true");
        } catch (InterruptedException | IOException e) {
            // Ensure the node process is killed.
            // InterruptedException is thrown when the main process exit.
            logger.info("killing webpack dev-server", e);
            if (process != null) {
                process.destroyForcibly();
            }
        }

    });

    thread.start();
}

其他回答

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

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

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

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

(cat && kill 0) | python

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

我把父pid用环境传递给子pid, 然后定期检查子进程中是否存在/proc/$ppid。

我认为不可能保证只使用标准POSIX调用。就像现实生活一样,一旦孩子被孕育出来,它就有了自己的生命。

父进程可以捕获大多数可能的终止事件,并尝试在此时终止子进程,但总有一些无法捕获。

例如,没有进程可以捕获SIGKILL。当内核处理这个信号时,它将杀死指定的进程,而不通知该进程。

扩展一下类比——唯一的另一种标准方式是,当孩子发现自己不再有父母时自杀。

使用prctl(2)有一种linux独有的方法——请参阅其他答案。

另一种Linux特有的方法是在一个新的PID名称空间中创建父进程。然后它将是该名称空间中的PID 1,当它退出时,它的所有子节点将立即被SIGKILL杀死。

不幸的是,为了创建一个新的PID名称空间,您必须拥有CAP_SYS_ADMIN。但是,这种方法非常有效,在初始启动父进程之后不需要对父进程或子进程进行任何实际更改。

请参见clone(2)、pid_namespaces(7)和unshare(2)。

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

prctl(PR_SET_PDEATHSIG, SIGHUP);

详见man 2 prctl。

编辑:这是linux专用的