我已经开始执行以下代码
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
Process p = pb.start();
}
catch (IOException ex) {}
现在,我需要知道我刚刚开始的进程的pid。
我已经开始执行以下代码
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
Process p = pb.start();
}
catch (IOException ex) {}
现在,我需要知道我刚刚开始的进程的pid。
Answers:
由于Java 9类Process
具有新方法long pid()
,因此它很简单
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
Process p = pb.start();
long pid = p.pid();
} catch (IOException ex) {
// ...
}
尚无公共API。参见Sun Bug 4244896,Sun Bug 4250622
解决方法:
Runtime.exec(...)
返回一个类型的对象
java.lang.Process
Process类是抽象的,您得到的是为您的操作系统设计的Process的某些子类。例如,在Mac上,它会返回java.lang.UnixProcess
一个名为的私有字段pid
。使用反射,您可以轻松获得该字段的值。诚然,这是一种hack,但可能会有所帮助。PID
无论如何,您需要什么?
此页面包含如何:
http://www.golesny.de/p/code/javagetpid
在Windows上:
Runtime.exec(..)
返回“ java.lang.Win32Process”的实例)或“ java.lang.ProcessImpl”
两者都有一个专用字段“句柄”。
这是该过程的操作系统句柄。您将必须使用此+ Win32 API来查询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;
}
getDeclaredField()
总是返回相同的实例(不是)。因此,我再也无法保留该声明了
在您的库中包含jna(“ JNA”和“ JNA Platform”),并使用以下功能:
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.WinNT;
import java.lang.reflect.Field;
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;
}
我想我已经找到了一个解决方案,在大多数平台上工作时看起来都非常安全。这是想法:
由于只检查子进程,因此不会被同一台计算机上的其他进程所困扰。JVM范围的互斥锁可以使您确定,新进程是正确的。
读取子进程列表比从进程对象获取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;
}
}
}
在我的测试中,所有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。如果是,则解析的输出ps
。
java.lang.ProcessImpl
。例如,在Windows上对我来说失败。
我使用了一种非便携式方法来从Process
对象中检索UNIX PID,这很容易遵循。
步骤1:
使用一些Reflection API调用来标识Process
目标服务器JRE上的实现类(请记住这Process
是一个抽象类)。如果您的UNIX实现类似于我的UNIX实现,您将看到一个实现类,该类具有一个名为pid
name的属性,其中包含该进程的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日志记录中获得的实现类和字段名称,编写一些代码来窃取Process
实现类,并使用Reflection API从中检索PID。下面的代码可根据我的UNIX风格为我工作。您可能需要调整EXPECTED_IMPL_CLASS_NAME
和EXPECTED_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
}
这不是一个通用的答案。
但是:有些程序,尤其是服务和长期运行的程序,会创建(或提供创建(可选))“ pid文件”的信息。
例如,LibreOffice提供--pidfile={file}
,请参阅文档。
我一直在寻找Java / Linux解决方案一些时间,但是PID(以我为例)就在眼前。
我相信唯一可移植的方法是通过另一个(父)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();
}
}
要使用它,只需创建一个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...);
有一个具有这种功能的开源库,它具有跨平台的实现:https : //github.com/OpenHFT/Java-Thread-Affinity
仅获取PID可能会过大,但是如果您想要其他东西,例如CPU和线程ID,特别是线程亲和力,它可能就足够了。
要获取当前线程的PID,只需调用Affinity.getAffinityImpl().getProcessId()
。
这是使用JNA实现的(请参阅arcsin的答案)。
一种解决方案是使用平台提供的特质工具:
private static String invokeLinuxPsProcess(String filterByCommand) {
List<String> args = Arrays.asList("ps -e -o stat,pid,unit,args=".split(" +"));
// Example output:
// Sl 22245 bpds-api.service /opt/libreoffice5.4/program/soffice.bin --headless
// Z 22250 - [soffice.bin] <defunct>
try {
Process psAux = new ProcessBuilder(args).redirectErrorStream(true).start();
try {
Thread.sleep(100); // TODO: Find some passive way.
} catch (InterruptedException e) { }
try (BufferedReader reader = new BufferedReader(new InputStreamReader(psAux.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
if (!line.contains(filterByCommand))
continue;
String[] parts = line.split("\\w+");
if (parts.length < 4)
throw new RuntimeException("Unexpected format of the `ps` line, expected at least 4 columns:\n\t" + line);
String pid = parts[1];
return pid;
}
}
}
catch (IOException ex) {
log.warn(String.format("Failed executing %s: %s", args, ex.getMessage()), ex);
}
return null;
}
免责声明:未经测试,但您知道了:
ps
列出流程,ps
。对于GNU / Linux和MacOS(或通常的UNIX之类的)系统,我使用了下面的方法,该方法效果很好:
private int tryGetPid(Process process)
{
if (process.getClass().getName().equals("java.lang.UNIXProcess"))
{
try
{
Field f = process.getClass().getDeclaredField("pid");
f.setAccessible(true);
return f.getInt(process);
}
catch (IllegalAccessException | IllegalArgumentException | NoSuchFieldException | SecurityException e)
{
}
}
return 0;
}
使用JNA,支持新旧JVM来获取进程ID
public static long getProcessId(Process p){
long pid = -1;
try {
pid = p.pid();
} catch (NoSuchMethodError e) {
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));
pid = 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);
pid = f.getLong(p);
f.setAccessible(false);
}
}
catch(Exception ex)
{
pid = -1;
}
}
return pid;
}