Как я могу заставить дочерний процесс завершиться, когда родительский?
Я запускаю дочерний процесс с ProcessBuilder, и мне нужно выйти из дочернего процесса, если родительский процесс делает. При нормальных обстоятельствах мой код правильно останавливает ребенка. Однако, если я заставлю ОС уничтожить родителя, ребенок продолжит работу.
Есть ли способ "привязать" дочерний процесс к родительскому процессу, чтобы он завершился, когда родительский процесс был убит?
Похожие вопросы:
10 ответов
Нет связи между дочерним процессом и его родителем. Они могут знать идентификатор процесса друг друга, но между ними нет жесткой связи. То, что вы говорите о сиротском процессе. И это проблема уровня ОС. Значение любого решения, вероятно, зависит от платформы.
Единственное, о чем я могу подумать, - это периодически проверять состояние ребенка, если он закрывается. Я не думаю, что это было бы все так надежно, хотя.
Хотя вы не можете защитить от принудительного прерывания (например, SIGKILL в Unix), вы можете защитить от других сигналов, которые приводят к завершению вашего родительского процесса (например, 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);
Вы можете просто начать чтение потока из 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 на стороне сервера в режиме принятия на родительском порте со случайным большим номером. Когда начинается дочерний процесс, передайте номер порта в качестве параметра (переменная среды, запись в базе данных и т. Д.). Пусть он создаст поток и откроет этот сокет. Нить сидит на сокете навсегда. Если связь когда-либо разорвется, попросите ребенка выйти.
или же
Создайте поток на родительском элементе, который постоянно обновляет дату обновления файла. (Как часто зависит от степени детализации между уничтожением и выключением.) У дочернего элемента есть поток, который отслеживает время обновления одного и того же файла. Если он не обновляется после определенного интервала, автоматически отключается.
Для одного дочернего процесса вы можете управлять этим путем инверсии дочерних / родительских отношений. Смотрите мой ответ на последующее воплощение этого вопроса.
Для окон, я придумал этот взломать опрос:
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;
}
Попробуйте отправить сигнал уничтожения дочернему процессу от родителя, когда родительский процесс останавливается
Как я проверял, если родитель убит, тогда ppid потомка станет 1. Поэтому, вероятно, мы можем убить любые процессы, у которых ppid = 1.
Получив идентификатор родительского процесса (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 идентификатором вашего процесса. Здесь /T убивает все процессы в дереве процессов соответствующего PID.
Вы можете использовать функцию java.lang.Runtime.exec для выполнения вышеуказанной команды.
PS: это решение будет работать только для ОС Windows.