48

我正在使用 ProcessBuilder 启动一个子进程,如果父进程退出,则需要子进程退出。在正常情况下,我的代码正确地停止了孩子。但是,如果我导致操作系统杀死父母,孩子将继续运行。

有没有办法将子进程“绑定”到父进程,这样当父进程被杀死时它就会退出?


类似的问题:

4

10 回答 10

42

虽然您无法防止硬中止(例如 Unix 上的 SIGKILL),但您可以防止其他导致父进程关闭的信号(例如 SIGINT)并清理子进程。您可以通过使用关闭挂钩来完成此操作:请参阅Runtime#addShutdownHook以及此处的相关 SO 问题。

您的代码可能如下所示:

String[] command;
final Process childProcess = new ProcessBuilder(command).start();

Thread closeChildThread = new Thread() {
    public void run() {
        childProcess.destroy();
    }
};

Runtime.getRuntime().addShutdownHook(closeChildThread); 
于 2008-11-07T16:58:18.157 回答
21

子进程与其父进程之间没有联系。他们可能知道彼此的进程 ID,但他们之间没有硬性联系。你在说一个孤儿进程。这是操作系统级别的问题。这意味着任何解决方案都可能取决于平台。

关于我能想到的唯一一件事就是让孩子定期检查其父母状态,如果父母关闭则退出。我不认为这会是那么可靠。

于 2008-11-06T17:21:13.630 回答
17

您可以简单地在子进程中启动从 System.in 读取的线程。如果您没有写信给您孩子的标准输入,那么没有其他人会这样做,并且该线程将“永远”阻塞。但是,如果父进程被杀死或以其他方式死亡,您将获得 EOF(或异常)。所以你也可以关闭孩子。(子进程以 java.lang.ProcessBuilder 开始)

于 2011-08-16T21:52:56.413 回答
8

提供与@tomkri 的答案类似的黑客方式,并提供演示代码。

如果您的子进程不需要使用输入流,只需将子进程输入流重定向到其父进程的输入流。然后在子进程中添加一个线程以始终读取输入流,当该线程无法从输入流中读取任何内容时,该子进程退出。所以父进程退出->父进程的输入流不存在->子进程的输入流不存在->子进程退出。

这是Java的演示代码。

父进程:

package process.parent_child;

import java.io.File;
import java.io.IOException;
import java.lang.ProcessBuilder.Redirect;

public class ParentProc {

    public static void main(String[] args) {
        System.out.println("I'm parent.");

        String javaHome = System.getProperty("java.home");
        String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
        ProcessBuilder builder = new ProcessBuilder(javaBin, "process.parent_child.ChildProc");

        // Redirect subprocess's input stream to this parent process's input stream.
        builder.redirectInput(Redirect.INHERIT);
        // This is just for see the output of child process much more easily.
        builder.redirectOutput(Redirect.INHERIT);
        try {
            Process process = builder.start();
            Thread.sleep(5000);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Parent exits.");
    }
}

子进程:

package process.parent_child;

import java.io.IOException;
import java.util.Scanner;

public class ChildProc {


    private static class StdinListenerThread extends Thread {

        public void run() {
            int c;
            try {
                c = System.in.read();
                while ( c != -1 ) {
                    System.out.print(c);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println("\nChild exits.");
            System.exit(0);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        System.out.println("I'm child process.");
        StdinListenerThread thread = new StdinListenerThread();
        thread.start();
        Thread.sleep(10000);
    }
}

通过以下命令运行此父进程后:

    java process.parent_child.ParentProc

你会看见

    I'm parent.
    I'm child process.
    Parent exits.
    xmpy-mbp:bin zhaoxm$
    Child exits

当父进程退出时,子进程立即退出。

于 2015-11-12T16:16:11.807 回答
4

正如您所发现的,操作系统允许您解决此问题。相反,创建一个由两个进程共享的资源。当父母中止资源时,孩子的反应是关闭。例如:

在随机高数端口上的父级上以接受模式创建一个带有服务器端 TCP/IP 套接字的线程。当孩子启动时,将端口号作为参数传递(环境变量、数据库条目等)。让它创建一个线程并打开该套接字。让线程永远坐在套接字上。如果连接断开,让孩子退出。

或者

在父节点上创建一个线程,不断更新文件的更新日期。(多久取决于你需要多少kill和shutdown之间的粒度。)孩子有一个线程来监控同一个文件的更新时间。如果在特定时间间隔后没有更新,则自动关闭。

于 2008-12-28T07:25:42.613 回答
2

对于单个子进程,您可以通过反转子/父关系来管理它。请参阅对这个问题的后续化身的回答。

于 2008-12-28T06:17:48.663 回答
1

对于 Windows,我想出了这个投票技巧:

static int getPpid(int pid) throws IOException {
    Process p = Runtime.getRuntime().exec("C:\\Windows\\System32\\wbem\\WMIC.exe process where (processid="+pid+") get parentprocessid");
    BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
    br.readLine();
    br.readLine();
    String ppid= br.readLine().replaceAll(" ","");
    return Integer.parseInt(ppid);
}
static boolean shouldExit() {
    try {
        String pid = ManagementFactory.getRuntimeMXBean().getName().split("@")[0];
        int ppid = getPpid(Integer.parseInt(pid));
        /* pppid */ getPpid(ppid);
    } catch (Exception e) {
        return true;
    } 
    return false;
}
于 2015-07-16T09:41:14.147 回答
0

当父进程停止时,尝试从父进程向子进程发送终止信号

于 2008-11-06T17:29:16.720 回答
-1

正如我测试的那样,如果父进程被杀死,那么子进程的 ppid 将变为 1。所以我们可能可以杀死任何 ppid = 1 的进程。

于 2012-06-22T18:50:22.677 回答
-1

一旦我们有了父进程的进程 ID (PID),我们就可以杀死所有的子进程。我们可以使用windows taskkill命令杀死任何由相应PID启动的进程。

您可以根据您的用例从以下帖子中找到 PID:https ://stackoverflow.com/Questions/4750470/how-to-get-pid-of-process-ive-just-started-within-java-程序

获得 PID 后,您需要做的就是通过发送以下命令让操作系统为您终止进程:“taskkill /F /T /PID P_id”。在这里将 P_id 替换为您的进程 ID。这里 /T 杀死对应 PID 的进程树中的所有进程。

您可以使用java.lang.Runtime.exec函数来执行上述命令。

PS:此解决方案仅适用于 Windows 操作系统。

于 2020-12-26T00:10:17.083 回答