Как я могу заставить дочерний процесс завершиться, когда родительский? - PullRequest
43 голосов
/ 06 ноября 2008

Я запускаю дочерний процесс с ProcessBuilder, и мне нужен дочерний процесс для выхода, если родительский процесс делает. При нормальных обстоятельствах мой код правильно останавливает ребенка. Однако, если я заставлю ОС уничтожить родителя, ребенок продолжит работу.

Есть ли способ "привязать" дочерний процесс к родительскому процессу, чтобы он завершился, когда родительский процесс был убит?


Похожие вопросы:

Ответы [ 9 ]

35 голосов
/ 07 ноября 2008

Хотя вы не можете защитить от принудительного прерывания (например, 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); 
20 голосов
/ 06 ноября 2008

Нет связи между дочерним процессом и его родителем. Они могут знать идентификатор процесса друг друга, но между ними нет жесткой связи. То, что вы говорите о бесхозном процессе . И это проблема уровня ОС. Значение любого решения, вероятно, зависит от платформы.

О единственной вещи, о которой я могу думать, - это чтобы ребенок периодически проверял статус своих родителей, выходя из него, если родитель отключился. Я не думаю, что это было бы настолько надежно.

16 голосов
/ 17 августа 2011

Вы можете просто начать чтение потока из System.in в дочернем процессе. Если вы не пишете в stdin вашего ребенка, никто больше этого не сделает, и поток заблокирует «навсегда». Но вы получите EOF (или исключение), если родительский процесс убит или умрет иначе. Таким образом, вы можете отключить ребенка. (Дочерний процесс начался с java.lang.ProcessBuilder)

5 голосов
/ 12 ноября 2015

Укажите способ хакера, аналогичный ответу от @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

Дочерний процесс завершается сразу после выхода из родительского процесса.

3 голосов
/ 28 декабря 2008

Как вы обнаружили, операционная система позволяет обойти эту проблему. Вместо этого создайте ресурс, общий для обоих процессов. Когда родитель прерывает ресурс, дочерний процесс реагирует выключением. Например:

Создать поток с сокетом TCP / IP на стороне сервера в режиме принятия на родительском порте с произвольным портом большого числа. Когда начинается дочерний процесс, передайте номер порта в качестве параметра (переменная среды, запись в базе данных и т. Д.). Пусть он создаст поток и откроет этот сокет. Нить сидит на сокете навсегда. Если соединение когда-либо разорвется, попросите ребенка выйти.

или

Создать ветвь для родителя, которая постоянно обновляет дату обновления файла. (Как часто зависит от степени детализации между уничтожением и выключением.) У дочернего элемента есть поток, который отслеживает время обновления одного и того же файла. Если он не обновляется после определенного интервала, автоматически отключается.

2 голосов
/ 28 декабря 2008

Для одного дочернего процесса вы можете управлять этим путем инверсии дочернего / родительского отношения. См. мой ответ о более позднем воплощении этого вопроса.

0 голосов
/ 16 июля 2015

Для окон я придумал этот взлом опроса:

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;
}
0 голосов
/ 22 июня 2012

Как я проверял, если родитель убит, тогда ppid ребенка станет 1. Так что, вероятно, мы можем убить любые процессы, у которых ppid = 1.

0 голосов
/ 06 ноября 2008

Попробуйте отправить сигнал уничтожения дочернему процессу от родителя, когда родительский процесс остановится

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...