Je lance un processus enfant avec ProcessBuilder et j'ai besoin de ce processus si le processus parent le fait. Dans des circonstances normales, mon code arrête correctement l'enfant. Cependant, si je force le système d'exploitation à tuer le parent, l'enfant continuera à s'exécuter.
Existe-t-il un moyen de "lier" le processus enfant au parent, de sorte qu'il se ferme lorsque le parent est tué?
Questions similaires:
Il n'y a pas de lien entre un processus enfant et son parent. Ils se connaissent peut-être l'un l'autre, mais il n'y a pas de lien solide entre eux. Qu'est-ce que vous parlez d'un processus orphelin . Et c'est une préoccupation au niveau du système d'exploitation. Cela signifie que toute solution dépend probablement de la plate-forme.
La seule chose à laquelle je peux penser est de demander à l’enfant de vérifier périodiquement son statut de parent, en se retirant si le parent s’arrête. Je ne pense pas que ce serait si fiable.
Bien que vous ne puissiez pas vous protéger contre un avortement brutal (par exemple SIGKILL sous Unix), vous pouvez vous protéger contre d’autres signaux entraînant l’arrêt de votre processus parent (par exemple SIGINT) et le nettoyage de votre processus enfant. Pour ce faire, utilisez des crochets d'arrêt: voir Runtime # addShutdownHook , ainsi qu'une question SO connexe ici .
Votre code pourrait ressembler à quelque chose comme ça:
String[] command;
final Process childProcess = new ProcessBuilder(command).start();
Thread closeChildThread = new Thread() {
public void run() {
childProcess.destroy();
}
};
Runtime.getRuntime().addShutdownHook(closeChildThread);
Vous pouvez simplement démarrer un fil en lisant à partir de System.in dans le processus enfant. Si vous n'écrivez pas en stdin de votre enfant, personne ne le fera et le fil bloquera "pour toujours". Mais vous obtiendrez un EOF (ou une exception) si le processus parent est tué ou meurt autrement. Ainsi, vous pouvez également arrêter l'enfant. (Processus enfant démarré avec Java.lang.ProcessBuilder)
Fournissez un moyen de piratage similaire à la réponse de @tomkri et fournissez également le code de démonstration.
Si votre processus enfant n'a pas besoin d'utiliser le flux d'entrée, redirigez simplement le flux d'entrée du processus enfant vers le flux d'entrée de son processus parent. Ajoutez ensuite un thread dans l'enfant pour toujours lire le flux d'entrée. Lorsque ce thread ne peut rien lire dans le flux d'entrée, ce processus enfant se ferme. Ainsi, le processus parent est fermé -> le flux d'entrée du parent n'existe pas -> le flux d'entrée de l'enfant n'existe pas -> le processus enfant est terminé.
Voici le code de démonstration tout en Java.
Processus parent:
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.");
}
}
Processus enfant:
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);
}
}
Après avoir exécuté ce processus parent à l'aide de la commande suivante:
Java process.parent_child.ParentProc
Tu verras
I'm parent.
I'm child process.
Parent exits.
xmpy-mbp:bin zhaoxm$
Child exits
Le processus enfant se termine immédiatement lorsque le processus parent est terminé.
Comme vous l'avez constaté, le système d'exploitation vous permet de contourner ce problème. Au lieu de cela, créez une ressource partagée par les deux processus. Lorsque le parent abandonne la ressource, l'enfant réagit en se fermant. Par exemple:
Créez un thread avec un socket TCP/IP côté serveur en mode accept sur le parent sur un port de nombre élevé aléatoire. Lorsque l’enfant démarre, transmettez le numéro de port en tant que paramètre (variable d’environnement, entrée de base de données, peu importe). Faites-le créer un thread et ouvrez ce socket. Le faire asseoir le fil pour toujours. Si la connexion est interrompue, demandez à l'enfant de quitter.
ou
Créez un thread sur le parent qui met continuellement à jour la date de mise à jour sur un fichier. (La fréquence dépend de la granularité requise entre kill et shutdown.) L'enfant dispose d'un fil qui surveille la mise à jour du même fichier. S'il ne se met pas à jour après un intervalle spécifique, arrêtez-vous automatiquement.
Pour un processus enfant unique, vous pouvez gérer cela en inversant la relation enfant/parent. Voir ma réponse à une incarnation ultérieure de cette question.
Comme j'ai testé, si le parent est tué, alors le ppid d'un enfant deviendra 1. Donc, nous pouvons probablement tuer tous les processus qui ont ppid = 1.
Pour Windows, je suis venu avec ce sondage bidouiller:
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;
}
Essayez d'envoyer un signal d'interruption au processus enfant à partir du parent lorsque le parent s'arrête