强制终止子进程的 Java 工具/方法

2022-09-04 03:37:00

我正在寻找一个Java工具/包/库,它将允许我强制杀死子进程。

此工具/包/库必须在 Windows 平台上工作(必需)。需要对Linux/Unix的支持。

我的问题

我的Java代码创建了一个子进程,该子进程根本不会对杀死子进程的标准Java方法做出反应:process.destroy(),并且,由于我没有子进程的源代码,因此我无法对其进行编程以更好地处理终止请求。

我尝试在调用 destroy() 之前关闭子进程的错误输入和输出流,并且没有效果。

我甚至尝试将ctrlBreak信号(char= 3)直接传递到child.getOutputStream()中,并再次收到相同的结果。

我最终设法找到的解决方法是:

  1. 在创建子项 PID 时获取其 PID 这可以在 Windows 中通过在子项创建之前和之后((getRuntime().exec("tasklist /v"))

  2. 使用孩子的 PID 在 Windows 中发出强制终止系统命令
    getRuntime().exec("taskkill /pid " + childPid + " /f")

但是 - 这是我不想调试和维护的复杂代码,加上问题本身,我毫不怀疑,以前许多其他Java开发人员都遇到过,这使我希望这样的Java工具/包/库已经存在。

我只是不知道它的名字...

PS:我的子进程是由 创建的,但我使用 ProcessBuilder 获得了相同的行为。Runtime.getRuntime().exec(cmd)


答案 1

有一种更精简的方法可以使用Java JNA来做到这一点。

这绝对适用于Windows和Linux,我假设您也可以为其他平台做同样的事情。

Java进程处理的最大问题是缺乏一种方法来获取使用untime.getRuntime().exec()启动的进程的进程ID。

假设你得到了一个进程的pid,你总是可以在linux中启动kill -9命令,或者使用类似的方法在Windows中杀死一个进程。

这里有一种方法可以为linux本地获取进程ID(从selenium框架借来,:)),并且在JNA的帮助下,这也可以为Windows完成(使用本机Windows API调用)。

要使它工作(对于Windows),您首先必须在JAVA NATIVE ACCESS(JNA)上获取JNA库:下载或从maven获取它

看看下面的代码,它将获得一个(在此示例windows中)程序的pid(大多数代码实际上是碎片,以使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

我过去曾使用您在此处建议的相同方法解决了此类问题:对Windows使用taskkill,对Unix使用kill -9。

在Windows上,您可以通过从Java调用脚本(VBS或JS)或使用互操作性库之一(JaWin,Jintegra,Jinterop等)来替代使用WMI。

我不认为这个解决方案像你害怕的那样复杂。我认为它不超过50行代码。

祝你好运。


推荐