ProcessBuilderを使用して子プロセスを起動していますが、親プロセスが終了する場合は子プロセスを終了する必要があります。通常の状況では、私のコードは子を適切に停止しています。ただし、OSに親を強制終了させると、子は実行を継続します。
親が殺されたときに終了するように、子プロセスを親に「結び付ける」方法はありますか?
同様の質問:
ProcessBuilderを使用して子プロセスを起動していますが、親プロセスが終了する場合は子プロセスを終了する必要があります。通常の状況では、私のコードは子を適切に停止しています。ただし、OSに親を強制終了させると、子は実行を継続します。
親が殺されたときに終了するように、子プロセスを親に「結び付ける」方法はありますか?
同様の質問:
ハード アボート (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);
子プロセスとその親の間に結びつきはありません。彼らはお互いにプロセスIDを知っているかもしれませんが、それらの間に強いつながりはありません。孤立したプロセスについて話していること。そして、それはOSレベルの問題です。つまり、ソリューションはおそらくプラットフォームに依存します。
私が考えることができる唯一のことは、子供に定期的に親のステータスをチェックさせ、親がシャットダウンした場合は終了することです。しかし、これはそれほど信頼できるとは思いません。
子プロセスでSystem.inからスレッドの読み取りを開始するだけです。子供のstdinに書き込みを行っていない場合、他の誰も書き込みを行わず、スレッドは「永久に」ブロックされます。ただし、親プロセスが強制終了されるか、そうでない場合は終了すると、EOF(または例外)が発生します。したがって、子をシャットダウンすることもできます。(子プロセスはjava.lang.ProcessBuilderで開始されました)
@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
親プロセスが終了すると、子プロセスはすぐに終了します。
ご存じのように、オペレーティング システムではこの問題を回避できます。代わりに、両方のプロセスで共有されるリソースを作成してください。親がリソースを中止すると、子はシャットダウンして反応します。例えば:
サーバー側の TCP/IP ソケットを使用して、ランダムな高い番号のポートの親で受け入れモードでスレッドを作成します。子プロセスの開始時に、ポート番号をパラメータ (環境変数、データベース エントリなど) として渡します。スレッドを作成し、そのソケットを開きます。スレッドがソケットに永遠に留まるようにします。接続が切断された場合は、子プロセスを終了させます。
また
ファイルの更新日を継続的に更新するスレッドを親に作成します。(頻度は、必要な kill と shutdown の間の粒度によって異なります。) 子には、同じファイルの更新時間を監視するスレッドがあります。特定の間隔が経過しても更新されない場合は、自動的にシャットダウンします。
単一の子プロセスの場合、子/親の関係を逆にすることでこれを管理できます。この質問の後の化身に対する私の回答を参照してください。
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 シグナルを送信してみてください
私がテストしたように、親が殺されると、子のppidは1になります。したがって、おそらくppid=1のプロセスをすべて殺すことができます。