我正在使用
Process P1= Runtime.exec(...)
创建进程 P1。我的进程 P1 正在创建另一个进程,例如 P2、P3....
然后我想杀死进程 P1 以及 P1 创建的所有进程,即 P2、P3...
P1.destroy()
仅杀死 P1,而不杀死其子进程。
我也用Google搜索了一下,发现这是一个Java bug: http://bugs.sun.com/view_bug.do?bug_id=4770092
有人对如何做有任何想法吗?
是的,这是一个错误,但如果你阅读评估,就会发现根本问题是在 Windows 上实现“杀死所有小孩”几乎是不可能的。
答案是
P1
需要自己负责整理。
我遇到了类似的问题,我启动了一个 PowerShell 进程,该进程启动了一个 Ping 进程,当我停止我的 Java 应用程序时,PowerShell 进程将会终止(我会使用
Process.destroy()
来终止它),但它创建的 Ping 进程不会.
经过一番尝试后,这个方法成功了:
private void stopProcess(Process process) {
process.descendants().forEach(new Consumer<ProcessHandle>() {
@Override
public void accept(ProcessHandle t) {
t.destroy();
}
});
process.destroy();
}
它会杀死给定的进程及其所有子进程。
PS:您需要Java 9才能使用
Process.descendants()
方法。
Java 没有公开任何有关进程孙子的信息,这是有充分理由的。如果您的子进程启动另一个进程,则由子进程来管理它们。
我建议其中之一
感谢 @Giacomo 在我之前建议 IPC。
您是否编写了其他进程的代码,或者它们是您无法更改的?
如果可以的话,我会考虑修改它们,以便它们接受某种消息(即使通过标准流),这样它们就可以根据请求很好地终止,如果有的话,可以自行终止。
我不认为“破坏过程”是干净的。
如果它是错误,正如你所说,那么你必须跟踪子进程的进程树,并在你想杀死父进程时杀死树中的所有子进程 如果你只有几个进程而不是使用列表,你需要使用数据结构树
为了杀死子进程,java进程实例不保证杀死所有相关的子进程。直到java 8,我们都没有正确的方法来获取进程的pid,因此您需要先执行以下操作来获取pid,然后对windows使用taskkill,对linux使用pkill。从java 9开始,你不需要担心pid。它只是 process.pid() 并使用该 pid 来终止进程。像taskkill /PID pid /F /T。
/**
* Method to destroy process and its children manually for both windows and linux
* @param process
* @param commandLine
*/
private static void destroyProcess(Process process, String commandLine) {
if (Platform.getOS().contains(Platform.OS_WIN32)) {
commandLine = commandLine.replace("\\", "\\\\");
// calling process.destroy() doesn't kill its subprocesses properly so getting id
// of the launched process through its commandLine and terminating it by running
// taskkill command with /T flag
String command = "powershell -command \" & {$proc_id=(Get-CimInstance Win32_Process -Filter \"\"\"CommandLine='" //$NON-NLS-1$
+ commandLine + "\"' AND ParentProcessId = " + getProcessId() //$NON-NLS-1$
+ "\"\"\"\").ProcessId;;taskkill /PID $proc_id /F /T}";
try {
Process p = Runtime.getRuntime().exec(command);
while (!p.waitFor(1, TimeUnit.SECONDS));
} catch (IOException | InterruptedException e) {
//log exception
}
}
else {
//linux case
try {
if (process.getClass().getName().equals("java.lang.UNIXProcess")) {
Field f = process.getClass().getDeclaredField("pid");
f.setAccessible(true);
int pid = (int) f.getLong(process);
f.setAccessible(false);
Process p = Runtime.getRuntime().exec("pkill -P " + pid);
while (!p.waitFor(1, TimeUnit.SECONDS));
}
} catch (Exception e) {
//log exception
}
}
}
/**
* Method to get process id of running application
* @return process id
*/
private static int getProcessId() {
RuntimeMXBean bean = ManagementFactory.getRuntimeMXBean();
// Get name representing the running Java virtual machine.
// It returns something like 6460@USER. Where the value
// before the @ symbol is the PID.
String jvmName = bean.getName();
return Integer.valueOf(jvmName.split("@")[0]);
}
因为 Runtime.exec() 返回 Process 的实例,您可以使用一些数组来存储它们的引用,并稍后通过 Process.destroy() 杀死它们。