web-dev-qa-db-ja.com

親が終了したときに子プロセスを終了させるにはどうすればよいですか?

ProcessBuilderで子プロセスを起動していますが、親プロセスが終了した場合に子プロセスを終了する必要があります。通常の状況では、私のコードは子供を適切に停止しています。ただし、OSに親を強制終了させて​​も、子は実行を続けます。

親が殺されたときに終了するように、子プロセスを親に「結び付ける」方法はありますか?


同様の質問:

45
Alex

子プロセスとその親の間には結びつきはありません。彼らはお互いのプロセスIDを知っているかもしれませんが、それらの間に強いつながりはありません。あなたが話しているのは 孤立プロセス です。そして、それはOSレベルの懸念です。ソリューションの意味はおそらくプラットフォームに依存します。

私が考えることができる唯一のことについては、子供に親の状態を定期的にチェックさせ、親のシャットダウンがあれば終了することです。しかし、これはそれほど信頼できるとは思いません。

20
sblundy

ハードアボート(UnixのSIGKILLなど)から保護することはできませんが、親プロセスをシャットダウン(SIGINTなど)して子プロセスをクリーンアップさせる他のシグナルからは保護できます。これは、シャットダウンフックを使用して実現できます。 Runtime#addShutdownHook と、関連するSO質問 here を参照してください。

コードは次のようになります。

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

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

Runtime.getRuntime().addShutdownHook(closeChildThread); 
36
Greg Case

子プロセスでSystem.inからスレッドの読み取りを開始するだけです。お子様の標準入力に書き込みを行っていない場合、他のユーザーは何もせず、スレッドは「永久に」ブロックします。ただし、親プロセスが強制終了されるか、そうでない場合は、EOF(または例外)が発生します。したがって、子もシャットダウンできます。(子プロセスはJava.lang.ProcessBuilderで開始されます。 )

16
tomkri

@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

親プロセスが終了すると、子プロセスはすぐに終了します。

5
xmpy

ご存じのとおり、オペレーティングシステムを使用すると、この問題を回避できます。代わりに、両方のプロセスで共有されるリソースを作成します。親がリソースを中止すると、子はシャットダウンして反応します。例えば:

ランダムな数の大きいポートの親で、受け入れモードのサーバー側TCP/IPソケットでスレッドを作成します。子が起動したら、ポート番号をパラメーター(環境変数、データベースエントリなど)として渡します。スレッドを作成して、そのソケットを開きます。スレッドをソケットに永久に配置します。接続が切断された場合、子を終了させます。

または

ファイルの更新日を継続的に更新するスレッドを親に作成します。 (どのくらいの頻度で、必要なkillとシャットダウンの粒度に依存します。)子には、同じファイルの更新時間を監視するスレッドがあります。特定の間隔が経過しても更新されない場合は、自動的にシャットダウンします。

3
jmucchiello

単一の子プロセスの場合、子/親の関係を逆にすることでこれを管理できます。この質問の後の化身については、 私の答え を参照してください。

2
dmckee

私がテストしたように、親が殺されると、子のppidは1になります。したがって、おそらくppid = 1のプロセスをすべて殺すことができます。

0
cache

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;
}

親が停止したときに、親から子プロセスにkillシグナルを送信してみてください

0
Raxvan