如何获得进程的PID我刚刚开始在Java程序内?

我已经用下面的代码开始了一个过程

ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path"); try { Process p = pb.start(); } catch (IOException ex) {} 

现在我需要知道我刚刚开始的进程的PID。

这个页面有HOWTO:

http://www.golesny.de/p/code/javagetpid

在Windows上:

 Runtime.exec(..) 

返回“java.lang.Win32Process”的实例)或“java.lang.ProcessImpl”

两者都有私人领域“处理”。

这是该进程的操作系统句柄。 你将不得不使用这个+ Win32 API来查询PID。 该页面有详细的如何做到这一点。

目前还没有公开的API。 请参阅Sun Bug 4244896 ,Sun Bug 4250622

作为解决方法:

 Runtime.exec(...) 

返回一个types的对象

 java.lang.Process 

Process类是抽象的,你得到的是Process的一些子类,它是为你的操作系统devise的。 例如,在Mac上,它返回java.lang.UnixProcess ,它有一个名为pid的私有字段。 使用reflection,你可以很容易地得到这个领域的价值。 这当然是一个黑客,但它可能会有所帮助。 无论如何你需要什么PID

在Unix系统(Linux和Mac)

  public static synchronized long getPidOfProcess(Process p) { long pid = -1; try { if (p.getClass().getName().equals("java.lang.UNIXProcess")) { Field f = p.getClass().getDeclaredField("pid"); f.setAccessible(true); pid = f.getLong(p); f.setAccessible(false); } } catch (Exception e) { pid = -1; } return pid; } 

由于Java 9Process有一个新的方法long pid() ,所以就像

 ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path"); try { Process p = pb.start(); long pid = p.getPid(); } catch (IOException ex) { // ... } 

我想我已经find了一个解决scheme,在大多数平台上工作的时候看起来相当不错。 这是这个想法:

  1. 创build一个在产生新进程/终止进程之前获得的JVM范围的互斥体
  2. 使用平台相关代码来获取JVM进程的subprocess+ pid列表
  3. 产生新的过程
  4. 获取新的subprocess+ pid列表,并与之前的列表进行比较。 新的是你的家伙。

由于您仅检查subprocess,因此您不能在同一台计算机上受到其他进程的委托。 JVM全互斥可以让你确信,新的过程是正确的。

读取subprocess列表比从进程对象获取PID要简单,因为它不需要在Windows上调用WIN API,更重要的是,它已经在几个库中完成了。

下面是使用JavaSysMon库的上述思想的一个实现。 它

 class UDKSpawner { private int uccPid; private Logger uccLog; /** * Mutex that forces only one child process to be spawned at a time. * */ private static final Object spawnProcessMutex = new Object(); /** * Spawns a new UDK process and sets {@link #uccPid} to it's PID. To work correctly, * the code relies on the fact that no other method in this JVM runs UDK processes and * that no method kills a process unless it acquires lock on spawnProcessMutex. * @param procBuilder * @return */ private Process spawnUDK(ProcessBuilder procBuilder) throws IOException { synchronized (spawnProcessMutex){ JavaSysMon monitor = new JavaSysMon(); DirectUDKChildProcessVisitor beforeVisitor = new DirectUDKChildProcessVisitor(); monitor.visitProcessTree(monitor.currentPid(), beforeVisitor); Set<Integer> alreadySpawnedProcesses = beforeVisitor.getUdkPids(); Process proc = procBuilder.start(); DirectUDKChildProcessVisitor afterVisitor = new DirectUDKChildProcessVisitor(); monitor.visitProcessTree(monitor.currentPid(), afterVisitor); Set<Integer> newProcesses = afterVisitor.getUdkPids(); newProcesses.removeAll(alreadySpawnedProcesses); if(newProcesses.isEmpty()){ uccLog.severe("There is no new UKD PID."); } else if(newProcesses.size() > 1){ uccLog.severe("Multiple new candidate UDK PIDs"); } else { uccPid = newProcesses.iterator().next(); } return proc; } } private void killUDKByPID(){ if(uccPid < 0){ uccLog.severe("Cannot kill UCC by PID. PID not set."); return; } synchronized(spawnProcessMutex){ JavaSysMon monitor = new JavaSysMon(); monitor.killProcessTree(uccPid, false); } } private static class DirectUDKChildProcessVisitor implements ProcessVisitor { Set<Integer> udkPids = new HashSet<Integer>(); @Override public boolean visit(OsProcess op, int i) { if(op.processInfo().getName().equals("UDK.exe")){ udkPids.add(op.processInfo().getPid()); } return false; } public Set<Integer> getUdkPids() { return udkPids; } } } 

在你的库中包含jna并使用这个函数:

 public static long getProcessID(Process p) { long result = -1; try { //for windows if (p.getClass().getName().equals("java.lang.Win32Process") || p.getClass().getName().equals("java.lang.ProcessImpl")) { Field f = p.getClass().getDeclaredField("handle"); f.setAccessible(true); long handl = f.getLong(p); Kernel32 kernel = Kernel32.INSTANCE; WinNT.HANDLE hand = new WinNT.HANDLE(); hand.setPointer(Pointer.createConstant(handl)); result = kernel.GetProcessId(hand); f.setAccessible(false); } //for unix based operating systems else if (p.getClass().getName().equals("java.lang.UNIXProcess")) { Field f = p.getClass().getDeclaredField("pid"); f.setAccessible(true); result = f.getLong(p); f.setAccessible(false); } } catch(Exception ex) { result = -1; } return result; } 

我使用非可移植的方法从Process对象中检索UNIX PID,这非常简单。

步骤1:使用一些Reflection API调用来标识目标服务器JRE上的Process实现类(请记住Process是一个抽象类)。 如果你的UNIX实现和我的一样,你会看到一个实现类,它有一个名为pid的属性,它包含了进程的PID。 这是我使用的日志代码。

  //-------------------------------------------------------------------- // Jim Tough - 2014-11-04 // This temporary Reflection code is used to log the name of the // class that implements the abstract Process class on the target // JRE, all of its 'Fields' (properties and methods) and the value // of each field. // // I only care about how this behaves on our UNIX servers, so I'll // deploy a snapshot release of this code to a QA server, run it once, // then check the logs. // // TODO Remove this logging code before building final release! final Class<?> clazz = process.getClass(); logger.info("Concrete implementation of " + Process.class.getName() + " is: " + clazz.getName()); // Array of all fields in this class, regardless of access level final Field[] allFields = clazz.getDeclaredFields(); for (Field field : allFields) { field.setAccessible(true); // allows access to non-public fields Class<?> fieldClass = field.getType(); StringBuilder sb = new StringBuilder(field.getName()); sb.append(" | type: "); sb.append(fieldClass.getName()); sb.append(" | value: ["); Object fieldValue = null; try { fieldValue = field.get(process); sb.append(fieldValue); sb.append("]"); } catch (Exception e) { logger.error("Unable to get value for [" + field.getName() + "]", e); } logger.info(sb.toString()); } //-------------------------------------------------------------------- 

第2步:根据从reflection日志logging中获得的实现类和字段名称,编写一些代码来拣选Process实现类,并使用Reflection API从中检索PID。 下面的代码适用于我的UNIX风格。 您可能需要调整EXPECTED_IMPL_CLASS_NAMEEXPECTED_PID_FIELD_NAME常数才能为您工作。

 /** * Get the process id (PID) associated with a {@code Process} * @param process {@code Process}, or null * @return Integer containing the PID of the process; null if the * PID could not be retrieved or if a null parameter was supplied */ Integer retrievePID(final Process process) { if (process == null) { return null; } //-------------------------------------------------------------------- // Jim Tough - 2014-11-04 // NON PORTABLE CODE WARNING! // The code in this block works on the company UNIX servers, but may // not work on *any* UNIX server. Definitely will not work on any // Windows Server instances. final String EXPECTED_IMPL_CLASS_NAME = "java.lang.UNIXProcess"; final String EXPECTED_PID_FIELD_NAME = "pid"; final Class<? extends Process> processImplClass = process.getClass(); if (processImplClass.getName().equals(EXPECTED_IMPL_CLASS_NAME)) { try { Field f = processImplClass.getDeclaredField( EXPECTED_PID_FIELD_NAME); f.setAccessible(true); // allows access to non-public fields int pid = f.getInt(process); return pid; } catch (Exception e) { logger.warn("Unable to get PID", e); } } else { logger.warn(Process.class.getName() + " implementation was not " + EXPECTED_IMPL_CLASS_NAME + " - cannot retrieve PID" + " | actual type was: " + processImplClass.getName()); } //-------------------------------------------------------------------- return null; // If PID was not retrievable, just return null } 

在我的testing中,所有的IMPL类都有“pid”字段。 这对我有效:

 public static int getPid(Process process) { try { Class<?> cProcessImpl = process.getClass(); Field fPid = cProcessImpl.getDeclaredField("pid"); if (!fPid.isAccessible()) { fPid.setAccessible(true); } return fPid.getInt(process); } catch (Exception e) { return -1; } } 

只要确保返回的值不是-1。 如果是,则parsingps的输出。

没有一个简单的解决scheme。 我过去的做法是启动另一个进程,在类Unix系统上运行ps命令或在Windows上运行tasklist命令,然后parsing该命令的输出以获得所需的PID。 实际上,我最终将每个平台的代码放到一个单独的shell脚本中,这样每个平台都返回了PID,这样我就可以尽可能保持Java平台的独立性。 这对于短期任务来说效果并不好,但这对我来说不是问题。

jnr进程项目提供了这个function。

它是由jruby使用的java本机运行时的一部分,可以被认为是未来的java-FFI的原型

我相信唯一可移植的方法是通过另一个(父)Java进程运行(子)进程,这将通知我父进程的实际PID。 孩子的过程可以是任何事情。

这个包装的代码是

 package com.panayotis.wrapper; import java.io.File; import java.io.IOException; import java.lang.management.ManagementFactory; public class Main { public static void main(String[] args) throws IOException, InterruptedException { System.out.println(ManagementFactory.getRuntimeMXBean().getName().split("@")[0]); ProcessBuilder pb = new ProcessBuilder(args); pb.directory(new File(System.getProperty("user.dir"))); pb.redirectInput(ProcessBuilder.Redirect.INHERIT); pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); pb.redirectError(ProcessBuilder.Redirect.INHERIT); pb.start().waitFor(); } } 

要使用它,用这个创build一个jar文件,然后用命令参数调用它:

 String java = System.getProperty("java.home") + separator + "bin" + separator + "java.exe"; String jar_wrapper = "path\\of\\wrapper.jar"; String[] args = new String[]{java, "-cp", jar_wrapper, "com.panayotis.wrapper.Main", actual_exec_args...); 

如果可移植性不是一个问题,并且您只是想在Windows上获得pid而没有太多的麻烦,而使用经过testing并且可以在所有现代版本的Windows上工作的代码,则可以使用kohsuke的winp库。 它也可以在Maven中心轻松使用。

 Process process = //...; WinProcess wp = new WinProcess(process); int pid = wp.getPid(); 

有一个开源的库有这样的function,它有跨平台的实现: https : //github.com/OpenHFT/Java-Thread-Affinity

获得PID可能会过度,但是如果你想要其他的东西,如CPU和线程ID,特别是线程亲和力,它可能已经足够。

要获取当前线程的PID,只需调用Affinity.getAffinityImpl().getProcessId()

这是使用JNA实现的(参见arcsin的答案)。

您可以从pipe理运行时bean中提取pid,如下所示:

 ManagementFactory.getRuntimeMXBean().getName() 

它是由pid @ hostname组成的,所以你可以用一个简单的正则expression式轻松获得pid。

对于运行时信息(可用内存,加载,可用的cpus等),可以使用:

 Runtime.getRuntime()