Инструмент/метод Java для принудительного уничтожения дочернего процесса

Я ищу инструмент/пакет/библиотеку Java, который позволит мне принудительно убить дочерний процесс.

Этот инструмент/пакет/библиотека должны работать на платформе Windows (обязательно). Рекомендуется поддержка Linux/Unix.

Моя проблема

Мой код Java создает дочерний процесс, который просто не реагирует на стандартный Java-способ для уничтожения дочернего процесса: process.destroy() и, так как у меня нет исходного кода для ребенка, я не могу запрограммировать его на лучше обрабатывать запросы на завершение.

Я попытался закрыть входной и выходной поток ошибок дочернего процесса перед вызовом destroy() и без эффекта.

Я даже пробовал передать сигнал ctrlBreak (char= 3) непосредственно в child.getOutputStream() и снова получили те же результаты.

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

  • Получите дочерний PID при его создании   Это можно сделать в Windows, используя список процессов   до и после создания ребенка (getRuntime().exec("tasklist /v"))

  • Используйте дочерний PID для выдачи команды системы принудительного уничтожения

          в Windows: getRuntime().exec("taskkill /pid " + childPid + " /f")

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

Я просто не знаю его имени...

PS: Мой дочерний процесс был создан Runtime.getRuntime().exec(cmd), но Я получаю такое же поведение, используя ProcessBuilder.

Ответы

Ответ 1

Есть более простой способ сделать это с помощью Java JNA.

Это определенно работает для Windows и Linux, я предполагаю, что вы можете сделать то же самое и для других платформ.

Самая большая проблема обработки Java-процессов - это отсутствие метода для получения идентификатора процесса, запущенного с помощью untime.getRuntime(). Exec().

Предполагая, что вы получили pid процесса, вы всегда можете запустить команду kill -9 в linux или использовать аналогичные способы для уничтожения процесса в windows.

Вот способ получения идентификатора процесса для linux (заимствован из платформы selenium, :)), и с помощью JNA это также можно сделать для окон (с использованием вызовов API Windows).

Чтобы это работало (для Windows), сначала вы должны получить библиотеку JNA на JAVA NATIVE ACCESS (JNA): Загрузите или получите ее от maven.

Посмотрите на следующий код, который получит pid программы (в этом примере Windows) (большая часть кода на самом деле является мусором для запуска работающей java-программы):

import com.sun.jna.*;
import java.lang.reflect.Field;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Main {

static interface Kernel32 extends Library {

    public static Kernel32 INSTANCE = (Kernel32) Native.loadLibrary("kernel32", Kernel32.class);

    public int GetProcessId(Long hProcess);
}

public static void main(String[] args) {
    try {
        Process p;

        if (Platform.isWindows())
            p = Runtime.getRuntime().exec("cmd /C ping msn.de");
        else if (Platform.isLinux())
            p = Runtime.getRuntime().exec("cmd /C ping msn.de");

        System.out.println("The PID: " + getPid(p));

        int x = p.waitFor();
        System.out.println("Exit with exitcode: " + x);

    } catch (Exception ex) {
        Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
    }
}

public static int getPid(Process p) {
    Field f;

    if (Platform.isWindows()) {
        try {
            f = p.getClass().getDeclaredField("handle");
            f.setAccessible(true);
            int pid = Kernel32.INSTANCE.GetProcessId((Long) f.get(p));
            return pid;
        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    } else if (Platform.isLinux()) {
        try {
            f = p.getClass().getDeclaredField("pid");
            f.setAccessible(true);
            int pid = (Integer) f.get(p);
            return pid;
        } catch (Exception ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    else{}
    return 0;
}
}

Надеюсь это поможет, ;)...

Ответ 2

Я решил эту проблему в прошлом, используя тот же метод, который вы предлагаете здесь: используйте taskkill для windows и kill -9 для Unix.

В окнах вы можете использовать альтернативный WMI путем вызова script (VBS или JS) с Java или с использованием одной из библиотек совместимости (JaWin, Jintegra, Jinterop и т.д.)

Я не думаю, что это решение настолько сложно, насколько вы боитесь. Я думаю, что это не более 50 строк кода.

Удачи.

Ответ 3

Для окон, использующих jna 3.5.1

try {
        Process p = Runtime.getRuntime.exec("notepad");

        Field f = p.getClass().getDeclaredField("handle");
        f.setAccessible(true);              
        long handLong = f.getLong(p);

            Kernel32 kernel = Kernel32.INSTANCE;

        WinNT.HANDLE handle = new WinNT.HANDLE();

        handle.setPointer(Pointer.createConstant(handLong));

        int pid = kernel.GetProcessId(handle);

        System.out.print(pid);
    } catch (Throwable e) {
}