天天看点

Linux系统下Java运行Process时,获取到其PID方法问题背景解决方法(前两种有缺陷)

某些方式需要运用到JDK9

问题背景

有时我们会调用控制台命令,来进行某些操作。而这些操作的进程有些是需要手动Kill掉的,那么问题来了,我们如何得到他的PID呢

解决方法(前两种有缺陷)

1、使用 ps -ef  命令获取所有PID,进行筛选

当我们运行ps -ef 的时候,会显示出一个列表,上标包括

UID   PID   PPID   C   STIME   TTY   TIME   CMD

我们需要的是PID这一字段,CMD为我们运行的代码段。

我们可以通过

BufferedReader br = new BufferedReader(new InputStreamReader(process.getInputStream()));            

String line = null;
while ((line = br.readLine()) != null) {
      System.out.println(new String(line.getBytes(), "GBK"));
}
           

这段代码来进行循环输出每一行,这样我们可以对每一行的数据按照我们所运行命令行的内容进行筛选,

然后对那一行的String进行处理,来获得其PID。这是其中一种方法。

但这样的话,如果我们有多个相同命令行的程序同步进行,那么这样可能会造成误伤。杀死一些无辜的进程。

2、使用反射机制

我们可以通过如下代码来获得 “PID”

long pid = -1;
Field field = null;
try {
       Class<?> clazz = Class.forName("java.lang.UNIXProcess");
       field = clazz.getDeclaredField("pid");
       field.setAccessible(true);
       pid = (Integer) field.get(process);
} 
catch (Throwable e) {
        e.printStackTrace();
}
           

但是这样获得的PID,会是linux执行该命令行窗口的PID,杀死该进程只是相当于把操作窗口关闭,而其代码还在继续运行解决不了根本的问题。

3、最终解决方案(需使用JDK9以上版本)

就如方法2所说,我们使用JNA只能获得其最顶部的进程,其实真正执行代码的进程,是窗口进程的子进程。

所以我们可以使用JDK9新增的API来解决这个问题。

代码如下:

//获取PID
List<Long> pids = new ArrayList<>();
Stream<ProcessHandle> stream = process.descendants();
List<ProcessHandle> list = stream.collect(Collectors.toList());
for (ProcessHandle processHandle:
           list) {
       pids.add(processHandle.pid());
}
pids.add(process.pid());
           

在JDK9中,我们可以拿到窗口进程的所有后代进程。这样我们就可以拿到正确的PID(们),依次进行KILL