Come posso far uscire un processo figlio quando lo fa il genitore?

Sto lanciando un processo figlio con ProcessBuilder e ho bisogno che il processo figlio esca se il processo padre lo fa. In circostanze normali, il mio codice sta fermando il bambino correttamente. Tuttavia, se il sistema operativo uccide il genitore, il figlio continuerà a funzionare.

C’è un modo per “bind” il processo figlio al genitore, in modo tale che esca quando il genitore viene ucciso?


Domande simili:

  • Come far morire il processo figlio dopo la chiusura del genitore?
  • I processi figli creati con fork () vengono automaticamente uccisi quando il genitore viene ucciso?

Non c’è legame tra un processo figlio e il suo genitore. Possono conoscersi l’ID del processo, ma non c’è una connessione difficile tra di loro. Di cosa stai parlando di un processo orfano . Ed è una preoccupazione di livello OS. Il significato di qualsiasi soluzione è probabilmente dipendente dalla piattaforma.

L’unica cosa che posso pensare è che il bambino controlli periodicamente lo stato dei suoi genitori, uscendo dall’arresto del genitore. Non penso che questo sarebbe tutto ciò che è affidabile.

Sebbene non sia ansible proteggersi da una dura interruzione (ad esempio SIGKILL su Unix), è ansible proteggersi da altri segnali che causano l’arresto del processo genitore (ad esempio SIGINT) e la pulizia del processo figlio. Puoi farlo usando i ganci di arresto: vedi Runtime # addShutdownHook , così come una domanda SO relativa qui .

Il tuo codice potrebbe apparire in questo modo:

String[] command; final Process childProcess = new ProcessBuilder(command).start(); Thread closeChildThread = new Thread() { public void run() { childProcess.destroy(); } }; Runtime.getRuntime().addShutdownHook(closeChildThread); 

È sufficiente avviare una lettura del thread da System.in nel processo figlio. Se non stai scrivendo per stdin di tuo figlio, nessun altro lo farà e il thread bloccherà “per sempre”. Ma otterrai un EOF (o un’eccezione), se il processo genitore viene ucciso o muore in altro modo. Quindi puoi spegnere anche il bambino. (Il processo figlio è iniziato con java.lang.ProcessBuilder)

Fornisci un metodo di hacker simile alla risposta di @tomkri e fornisci anche il codice demo.

Se il processo figlio non ha bisogno di utilizzare il stream di input, è sufficiente redirect il stream di input del processo figlio al stream di input del processo principale. Quindi aggiungi un thread in child per leggere sempre il stream di input e quando questo thread non può leggere nulla dal stream di input, questo processo figlio si chiude. Quindi il processo genitore termina -> il stream di input del genitore non esiste -> il stream di input del bambino non esiste -> le uscite del processo figlio.

Ecco il codice demo tutto in Java.

Processo genitore:

 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."); } } 

Processo figlio:

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

Dopo aver eseguito questo processo genitore con il seguente comando:

  java process.parent_child.ParentProc 

Vedrai

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

Il processo figlio termina immediatamente quando termina il processo genitore.

Come hai scoperto, il sistema operativo ti consente di risolvere questo problema. Invece, crea una risorsa condivisa da entrambi i processi. Quando il genitore abortisce la risorsa, il bambino reagisce chiudendo. Per esempio:

Creare un thread con un socket TCP / IP sul lato server in modalità di accettazione sul genitore su una porta numerica di numeri casuali. All’avvio del bambino, passare il numero di porta come parametro (variabile di ambiente, voce del database, qualunque sia). Farlo creare una discussione e aprire quella presa. Il thread è seduto sul socket per sempre. Se la connessione si interrompe, fai uscire il bambino.

o

Creare un thread su padre che aggiorna continuamente la data di aggiornamento su un file. (Quanto spesso dipende dalla granularità tra kill e shutdown di cui hai bisogno.) Il bambino ha un thread che monitora il tempo di aggiornamento dello stesso file. Se non si aggiorna dopo un intervallo specifico, si spegne automaticamente.

Per i processi a figlio unico è ansible gestirlo invertendo la relazione figlio / genitore. Vedi la mia risposta a una incarnazione successiva di questa domanda.

Prova a inviare un segnale kill al processo figlio dal genitore quando il genitore si ferma

Come ho provato, se il genitore viene ucciso, il ppid di un bambino diventerà 1. Quindi probabilmente possiamo uccidere qualsiasi processo che abbia ppid = 1.

Per Windows, ho trovato questo hack polling:

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