java - ejemplos - runtime getruntime();
¿Cómo puedo obtener el PID del proceso? Acabo de comenzar dentro del programa java. (16)
Empecé un proceso con el siguiente código
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
Process p = pb.start();
}
catch (IOException ex) {}
Ahora necesito saber el pid del proceso que acabo de comenzar.
Aún no hay una API pública para esto. ver Sun Bug 4244896 , Sun Bug 4250622
Como una solución alternativa:
Runtime.exec(...)
devuelve un objeto de tipo
java.lang.Process
La clase Process es abstracta, y lo que obtiene es una subclase de Process diseñada para su sistema operativo. Por ejemplo, en Mac, devuelve java.lang.UnixProcess
que tiene un campo privado llamado pid
. Usando Reflection puedes obtener fácilmente el valor de este campo. Esto es ciertamente un truco, pero podría ayudar. ¿Para qué necesitas el PID
de todos modos?
Creo que he encontrado una solución que parece bastante a prueba de balas mientras se trabaja en la mayoría de las plataformas. Aquí está la idea:
- Cree un mutex ancho de JVM que adquiera antes de generar un nuevo proceso / matar un proceso
- Use el código dependiente de la plataforma para adquirir la lista de procesos secundarios + pids de su proceso JVM
- Engendrar nuevo proceso
- Adquiera una nueva lista de procesos secundarios + pids y compárela con la lista anterior. El que es nuevo es tu hombre.
Como solo verifica procesos secundarios, no puede ser perjudicado por otro proceso en la misma máquina. Mútex de JVM que te permite estar seguro de que el nuevo proceso es el correcto.
Leer la lista de procesos secundarios es más simple que obtener el PID de los objetos de proceso, ya que no requiere llamadas a la API WIN en Windows y, lo que es más importante, ya se ha realizado en varias librerías.
A continuación se muestra una implementación de la idea anterior utilizando la biblioteca JavaSysMon . Eso
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;
}
}
}
Creo que la única forma portátil de hacerlo es ejecutar un proceso (secundario) a través de otro proceso Java (principal), que me informará el PID real del proceso principal. El proceso del niño podría ser cualquier cosa.
El código de este envoltorio es
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();
}
}
Para usarlo, crea un archivo jar con solo este, y llámalo con argumentos de comando de esta manera:
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...);
Dado que Java 9 class Process
tiene un nuevo método long pid()
, entonces es tan simple como
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
Process p = pb.start();
long pid = p.pid();
} catch (IOException ex) {
// ...
}
En Unix System (Linux y 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;
}
En mi prueba, todas las clases IMPL tenían el campo "pid". Esto me ha funcionado:
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;
}
}
Solo asegúrate de que el valor devuelto no sea -1. Si es así, luego analizar la salida de ps
.
Esta no es una respuesta genérica.
Sin embargo: algunos programas, especialmente servicios y programas de larga ejecución, crean (u ofrecen para crear, opcionalmente) un "archivo pid".
Por ejemplo, LibreOffice ofrece --pidfile={file}
, vea los docs .
Estaba buscando bastante tiempo para una solución Java / Linux pero el PID estaba (en mi caso) a la mano.
Esta página tiene el CÓMO:
http://www.golesny.de/p/code/javagetpid
En Windows:
Runtime.exec(..)
Devuelve una instancia de "java.lang.Win32Process") O "java.lang.ProcessImpl"
Ambos tienen un "control" de campo privado.
Este es un controlador de sistema operativo para el proceso. Tendrá que usar esta API + Win32 para consultar PID. Esa página tiene detalles sobre cómo hacer eso.
Hay una biblioteca de código abierto que tiene dicha función, y tiene implementaciones multiplataforma: https://github.com/OpenHFT/Java-Thread-Affinity
Puede ser exagerado solo para obtener el PID, pero si quiere otras cosas como CPU e ID de subproceso, y específicamente la afinidad de subprocesos, puede ser adecuado para usted.
Para obtener el PID del subproceso actual, simplemente llame a Affinity.getAffinityImpl().getProcessId()
.
Esto se implementa usando JNA (ver la respuesta de arcsin).
Incluya jna en su biblioteca y use esta función:
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;
}
No hay una solución simple. La forma en que lo hice en el pasado es iniciar otro proceso para ejecutar el comando ps
en sistemas tipo Unix, o el comando tasklist
en Windows, y luego analizar el resultado de ese comando para el PID que deseo. En realidad, terminé poniendo ese código en un script de shell separado para cada plataforma que acaba de devolver el PID, para poder mantener la pieza de Java como plataforma lo más independiente posible. Esto no funciona bien para tareas de corta duración, pero eso no fue un problema para mí.
Puede extraer el pid del bean de tiempo de ejecución de administración como en:
ManagementFactory.getRuntimeMXBean().getName()
Está compuesto por pid @ hostname para que pueda obtener el pid fácilmente con una expresión regular simple.
Para información de tiempo de ejecución (memoria libre, carga, cpus disponible, etc.) puede usar:
Runtime.getRuntime()
Si la portabilidad no es una preocupación, y solo desea obtener el pid en Windows sin demasiada molestia mientras usa el código probado y que funciona en todas las versiones modernas de Windows, puede usar la biblioteca winp de winp . También está disponible en Maven Central para un fácil consumo.
Process process = //...;
WinProcess wp = new WinProcess(process);
int pid = wp.getPid();
Una solución es usar las herramientas idiosincrásicas que ofrece la plataforma:
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;
}
Descargo de responsabilidad: No probado, pero se entiende la idea:
- Llama a
ps
para enumerar los procesos, - Encuentra el tuyo porque conoces el comando con el que lo lanzaste.
- Si hay múltiples procesos con el mismo comando, puede:
- Agregue otro argumento ficticio para diferenciarlos
- Confíe en el aumento de PID (no realmente seguro, no concurrente)
- Compruebe el momento de la creación del proceso (podría ser demasiado grosero para diferenciar realmente, tampoco concurrente)
- Agregue una variable de entorno específica y la liste con
ps
también.
el proyecto jnr-process proporciona esta capacidad.
Es parte del tiempo de ejecución nativo de Java utilizado por jruby y puede considerarse un prototipo para un futuro java-FFI
Usé un enfoque no portátil para recuperar el PID de UNIX del objeto Process
que es muy simple de seguir.
PASO 1: Use algunas llamadas de Reflection API para identificar la clase de implementación del Process
en el JRE del servidor de destino (recuerde que Process
es una clase abstracta). Si su implementación de UNIX es como la mía, verá una clase de implementación que tiene una propiedad llamada pid
que contiene el PID del proceso. Aquí está el código de registro que utilicé.
//--------------------------------------------------------------------
// 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());
}
//--------------------------------------------------------------------
PASO 2: en función de la clase de implementación y el nombre de campo que obtuvo del registro de reflexión, escriba un código para robar la clase de implementación del Process
y recuperar el PID a partir de él utilizando la API de reflexión. El código siguiente funciona para mí en mi sabor de UNIX. Es posible que deba ajustar las constantes EXPECTED_IMPL_CLASS_NAME
y EXPECTED_PID_FIELD_NAME
para que funcione.
/**
* 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
}