如何在Java中使用SIGKILL杀死Linux进程? Process.destroy()只会发送SIGTERM信号。

18
当我在Linux上运行destroy函数来终止java.lang.Process对象(实际上是真正的类型为java.lang.UNIXProcess)时,它会向进程发送一个SIGTERM信号,有没有一种方法可以使用SIGKILL杀死它?
4个回答

15

不使用纯Java。

最简单的替代方案是使用Runtime.exec()作为外部进程运行kill -9 <pid>命令。

不幸的是,很难获取PID。 你需要使用反射技巧访问private int pid字段,或者对ps命令的输出进行处理。

更新 - 实际上,还有另一种方法。创建一个小型工具(C程序、shell脚本等),用于运行真正的外部应用程序。编写该实用程序以便记住子进程的PID,并设置一个SIGTERM信号处理程序来SIGKILL子进程。


1
如果你要进行系统库调用,请不要直接在JNI中进行...为了避免一些麻烦,请使用JNA来完成。 - rob
不知道它是否可移植,但在Android上,Process.toString()会产生字符串Process[pid=1234] - Alexander Malakhov
虽然我对此感到不舒服,在我的代码中我选择“搞乱ps的输出” :) - Alexander Malakhov
1
@AlexanderMalakhov - 不,它不是可移植的,因为toString()方法的javadoc没有指定字符串的格式。根据源代码,在Unix上的Oracle Java中,ProcessUnixProcesstoString()方法没有从Object.toString()进行覆盖。你所看到的(最好的情况下)是特定于Android的。 - Stephen C
@Ercksen - 你是否检查了本地源代码以确定“forcibly”确实意味着SIGTERM? - Stephen C
显示剩余8条评论

11

Stephen的答案是正确的。我写了和他说的一样:

public static int getUnixPID(Process process) throws Exception
{
    System.out.println(process.getClass().getName());
    if (process.getClass().getName().equals("java.lang.UNIXProcess"))
    {
        Class cl = process.getClass();
        Field field = cl.getDeclaredField("pid");
        field.setAccessible(true);
        Object pidObject = field.get(process);
        return (Integer) pidObject;
    } else
    {
        throw new IllegalArgumentException("Needs to be a UNIXProcess");
    }
}

public static int killUnixProcess(Process process) throws Exception
{
    int pid = getUnixPID(process);
    return Runtime.getRuntime().exec("kill " + pid).waitFor();
}

你也可以通过这种方式获取pid:

public static int getPID() {
  String tmp = java.lang.management.ManagementFactory.getRuntimeMXBean().getName();
  tmp = tmp.split("@")[0];
  return Integer.valueOf(tmp);
}

在Linux上,我通过Java(交互式进程)运行命令:nslookup -> google,这会挂起进程,我无法终止该进程。所以现在我使用了您的建议,但它并没有终止该进程,它只是挂起了,我无法终止我的程序。我甚至尝试手动杀死PID,但无效,我该怎么办??? - Space Rocker
@Space Rocker:一个简单的kill命令会发送SIGTERM信号。而"kill -9"则会发送SIGKILL信号。尝试执行"kill -9 -1"命令:它会向进程组中所有pid > 1的进程发送SIGKILL信号。如果这样还不能杀死进程,那就没有其他方法了。但是这个方法一定有效。 - Csaba Toth
明确使用第二种方法获取自身 PID。VisualVM也是如此。前一种方法是丑陋的黑客方式,不跨平台,并且在未来的Java版本中可能无法工作。 - Espinosa
@Espinosa - 第二种方法也不具备可移植性。getName的javadoc说:“返回的名称字符串可以是任意字符串,Java虚拟机实现可以选择在返回的名称字符串中嵌入特定于平台的有用信息。”注意:可以选择,这意味着它也可以选择不这样做。而且没有规范说明该信息应如何格式化。 - Stephen C
从Java 9开始,您只需调用process.pid()即可获取进程ID。 - Luke Hutchison

1
如果您知道进程名称,您可以使用 pkill 命令。
Runtime.getRuntime().exec("pkill firefox").waitFor();

1

自Java 1.8版本开始

您可以调用destroyForcibly()方法,该方法默认调用destroy()方法,但根据Java文档,由ProcessBuilderRuntime.exec()返回的所有子进程都实现了此方法。


1
不幸的是,目前的实现只是调用了 destroy()。请参见此处:https://bugs.openjdk.java.net/browse/JDK-8056139 - Joe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接