Come ottenere il PID del processo Ho appena iniziato all’interno del programma java?

Ho iniziato un processo con il seguente codice

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

Ora ho bisogno di conoscere il pid del processo che ho appena iniziato.

Questa pagina ha l’HOWTO:

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

Su Windows:

 Runtime.exec(..) 

Restituisce un’istanza di “java.lang.Win32Process”) OPPURE “java.lang.ProcessImpl”

Entrambi hanno un “handle” di campo privato.

Questo è un handle del sistema operativo per il processo. Dovrai usare questa + API Win32 per interrogare il PID. Quella pagina ha dettagli su come farlo.

Non ci sono ancora API pubbliche per questo. vedi Sun Bug 4244896 , Sun Bug 4250622

Come soluzione:

 Runtime.exec(...) 

restituisce un object di tipo

 java.lang.Process 

La class Process è astratta e ciò che si ottiene è una sottoclass di Process progettata per il proprio sistema operativo. Ad esempio su Mac, restituisce java.lang.UnixProcess che ha un campo privato chiamato pid . Usando Reflection puoi facilmente ottenere il valore di questo campo. Questo è certamente un trucco, ma potrebbe essere d’aiuto. Che cosa ti serve comunque per il PID ?

Nel sistema Unix (Linux e 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; } 

Poiché il Process class Java 9 ha un nuovo metodo long pid() , quindi è semplice come

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

Penso di aver trovato una soluzione, che sembra abbastanza a prova di proiettile mentre si lavora sulla maggior parte delle piattaforms. Ecco l’idea:

  1. Crea un mutex di JVM che acquisisci prima di generare nuovo processo / uccidere un processo
  2. Utilizza il codice dipendente dalla piattaforma per acquisire l’elenco dei processi figli + pids del tuo processo JVM
  3. Crea un nuovo processo
  4. Acquisire un nuovo elenco di processi figlio + pids e confrontarli con l’elenco precedente. Quello che è nuovo è il tuo ragazzo.

Dato che controlli solo i processi figli, non puoi essere danneggiato da qualche altro processo nella stessa macchina. Mutex di dimensioni JVM che consente di essere sicuri che il nuovo processo sia corretto.

Leggere l’elenco dei processi figlio è più semplice che ottenere PID dagli oggetti processo, perché non richiede chiamate API WIN su Windows e, cosa più importante, è già stato fatto in diverse librerie.

Di seguito è una implementazione della suddetta idea utilizzando la libreria JavaSysMon . esso

 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 alreadySpawnedProcesses = beforeVisitor.getUdkPids(); Process proc = procBuilder.start(); DirectUDKChildProcessVisitor afterVisitor = new DirectUDKChildProcessVisitor(); monitor.visitProcessTree(monitor.currentPid(), afterVisitor); Set 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 udkPids = new HashSet(); @Override public boolean visit(OsProcess op, int i) { if(op.processInfo().getName().equals("UDK.exe")){ udkPids.add(op.processInfo().getPid()); } return false; } public Set getUdkPids() { return udkPids; } } } 

Includi jna nella tua libreria e usa questa funzione:

 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; } 

Ho usato un approccio non portatile per recuperare il PID UNIX dall’object Process che è molto semplice da seguire.

FASE 1: utilizzare alcune chiamate all’API di Reflection per identificare la class di implementazione del Process sul server di destinazione JRE (ricordare che Process è una class astratta). Se la tua implementazione UNIX è come la mia, vedrai una class di implementazione che ha una proprietà denominata pid che contiene il PID del processo. Ecco il codice di registrazione che ho usato.

  //-------------------------------------------------------------------- // 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()); } //-------------------------------------------------------------------- 

PASSAGGIO 2: in base alla class di implementazione e al nome del campo ottenuti dalla registrazione di Reflection, scrivere un codice per borseggiare la class di implementazione del Process e recuperare il PID da esso utilizzando l’API di Reflection. Il codice qui sotto funziona per me sul mio sapore di UNIX. Potrebbe essere necessario regolare le costanti EXPECTED_IMPL_CLASS_NAME e EXPECTED_PID_FIELD_NAME per farlo funzionare al posto tuo.

 /** * 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 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 } 

Nel mio test tutte le classi IMPL avevano il campo “pid”. Questo ha funzionato per me:

 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; } } 

Assicurati solo che il valore restituito non sia -1. Se lo è, analizza l’output di ps .

Questa non è una risposta generica.

Tuttavia: alcuni programmi, in particolare servizi e programmi di lunga durata, creano (o offrono la possibilità di creare, facoltativamente) un “file pid”.

Ad esempio, LibreOffice offre --pidfile={file} , vedi i documenti .

Stavo cercando un po ‘di tempo per una soluzione Java / Linux ma il PID era (nel mio caso) a portata di mano.

Non c’è una soluzione semplice. Il modo in cui l’ho fatto in passato è avviare un altro processo per eseguire il comando ps su sistemi Unix, o il comando tasklist su Windows, e quindi analizzare l’output di quel comando per il PID che voglio. In realtà, ho finito per inserire quel codice in uno script shell separato per ogni piattaforma che restituiva il PID, in modo da poter mantenere il pezzo Java il più indipendente ansible dalla piattaforma. Questo non funziona bene per compiti di breve durata, ma non era un problema per me.

il progetto jnr-process fornisce questa funzionalità.

Fa parte del runtime java nativo utilizzato da jruby e può essere considerato un prototipo per una futura java-FFI

Credo che l’unico modo portatile per farlo sia quello di eseguire un processo (figlio) attraverso un altro processo (genitore) Java, che mi informsrà sull’effettivo PID del processo genitore. Il processo figlio potrebbe essere qualsiasi cosa.

Il codice di questo wrapper è

 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(); } } 

Per usarlo, crea un file jar con solo questo, e chiamalo con argomenti comando questo:

 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...); 

Se la portabilità non è un problema, e vuoi solo ottenere il pid su Windows senza un sacco di problemi mentre usi il codice che è testato e noto per funzionare su tutte le moderne versioni di Windows, puoi usare la libreria winp di kohsuke . È anche disponibile su Maven Central per un facile consumo.

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

Esiste una libreria open-source con tale funzione e ha implementazioni multipiattaforma: https://github.com/OpenHFT/Java-Thread-Affinity

Potrebbe essere eccessivo solo per ottenere il PID, ma se vuoi altre cose come CPU e ID thread e in particolare affinità di thread, potrebbe essere adeguato per te.

Per ottenere il PID del thread corrente, chiama Affinity.getAffinityImpl().getProcessId() .

Questo è implementato usando JNA (vedi la risposta di arcsin).

Una soluzione è utilizzare gli strumenti idiosincratici offerti dalla piattaforma:

 private static String invokeLinuxPsProcess(String filterByCommand) { List 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]  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; } 

Disclaimer: non testato, ma hai l'idea:

  • Chiama ps per elencare i processi,
  • Trova il tuo perché conosci il comando con cui lo hai lanciato.
  • Se ci sono più processi con lo stesso comando, puoi:
    • Aggiungi un altro argomento fittizio per differenziarli
    • Affidati al PID crescente (non proprio sicuro, non concorrente)
    • Verifica l'ora della creazione del processo (potrebbe essere troppo grossolana per differenziarsi davvero, anche non simultaneamente)
    • Aggiungi una variabile d'ambiente specifica ed ps anche con ps .

È ansible estrarre il pid dal bean di runtime di gestione come in:

 ManagementFactory.getRuntimeMXBean().getName() 

È composto da pid @ hostname in modo da poter ottenere facilmente il pid con una semplice regex.

Per informazioni di runtime (memoria libera, carico, cpus disponibili, ecc.) Puoi usare:

 Runtime.getRuntime()