Ich habe einen Prozess mit folgendem Code gestartet
ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
Process p = pb.start();
}
catch (IOException ex) {}
Jetzt muss ich die PID des Prozesses kennen, die ich gerade begonnen habe.
Antworten:
Da die Java 9- Klasse
Process
eine neue Methode hatlong pid()
, ist sie so einfach wieProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path"); try { Process p = pb.start(); long pid = p.pid(); } catch (IOException ex) { // ... }
quelle
Hierfür gibt es noch keine öffentliche API. siehe Sun Bug 4244896 , Sun Bug 4250622
Um dieses Problem zu umgehen:
Gibt ein Objekt vom Typ zurück
Die Process-Klasse ist abstrakt, und Sie erhalten eine Unterklasse von Process zurück, die für Ihr Betriebssystem entwickelt wurde. Auf Macs wird beispielsweise
java.lang.UnixProcess
ein privates Feld mit dem Namen "Private" zurückgegebenpid
. Mit Reflection können Sie den Wert dieses Feldes leicht ermitteln. Dies ist zwar ein Hack, aber es könnte helfen. Wofür brauchst du das überhauptPID
?quelle
Diese Seite hat das HOWTO:
http://www.golesny.de/p/code/javagetpid
Unter Windows:
Gibt eine Instanz von "java.lang.Win32Process") ODER "java.lang.ProcessImpl" zurück.
Beide haben ein privates Feld "Handle".
Dies ist ein Betriebssystem-Handle für den Prozess. Sie müssen diese + Win32-API verwenden, um die PID abzufragen. Diese Seite enthält Details dazu.
quelle
In Unix System (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; }
quelle
getDeclaredField()
immer dieselbe Instanz zurückgibt (das tut es nicht). Also kann ich diese Aussage nicht mehr haltenFügen Sie jna (beide „JNA“ und „JNA Platform“) in Ihrer Bibliothek und verwenden Sie diese Funktion:
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; }
Sie können JNA auch von hier und JNA Platform von hier herunterladen .
quelle
Ich glaube, ich habe eine Lösung gefunden, die bei der Arbeit auf den meisten Plattformen ziemlich kugelsicher aussieht. Hier ist die Idee:
Da Sie nur nach untergeordneten Prozessen suchen, können Sie von keinem anderen Prozess auf demselben Computer falsch behandelt werden. Mit JVM-weitem Mutex können Sie sicher sein, dass der neue Prozess der richtige ist.
Das Lesen der untergeordneten Prozessliste ist einfacher als das Abrufen der PID von Prozessobjekten, da für Windows keine WIN-API-Aufrufe erforderlich sind und, was noch wichtiger ist, dies bereits in mehreren Bibliotheken durchgeführt wurde.
Im Folgenden finden Sie eine Implementierung der obigen Idee unter Verwendung der JavaSysMon- Bibliothek. Es
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; } } }
quelle
In meinen Tests hatten alle IMPL-Klassen das Feld "pid". Das hat bei mir funktioniert:
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; } }
Stellen Sie einfach sicher, dass der zurückgegebene Wert nicht -1 ist. Wenn dies der Fall ist, analysieren Sie die Ausgabe von
ps
.quelle
java.lang.ProcessImpl
. Es schlägt für mich zum Beispiel unter Windows fehl.Ich habe einen nicht portablen Ansatz verwendet, um die UNIX-PID von dem
Process
Objekt abzurufen, das sehr einfach zu befolgen ist.SCHRITT 1: Verwenden Sie einige Reflection-API-Aufrufe, um die
Process
Implementierungsklasse auf der Zielserver-JRE zu identifizieren (denken Sie daran, dass diesProcess
eine abstrakte Klasse ist). Wenn Ihre UNIX-Implementierung meiner entspricht, wird eine Implementierungsklasse mit einer Eigenschaft namenspid
angezeigt, die die PID des Prozesses enthält. Hier ist der Protokollierungscode, den ich verwendet habe.//-------------------------------------------------------------------- // 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()); } //--------------------------------------------------------------------
SCHRITT 2: Schreiben Sie basierend auf der Implementierungsklasse und dem Feldnamen, die Sie aus der Reflection-Protokollierung erhalten haben, Code, um die
Process
Implementierungsklasse zu stehlen und die PID mithilfe der Reflection-API daraus abzurufen. Der folgende Code funktioniert für mich in Bezug auf UNIX. Möglicherweise müssen Sie die KonstantenEXPECTED_IMPL_CLASS_NAME
und anpassenEXPECTED_PID_FIELD_NAME
, damit es für Sie funktioniert./** * 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 }
quelle
Dies ist keine generische Antwort.
Allerdings: Einige Programme, insbesondere Dienste und Programme mit langer Laufzeit, erstellen (oder bieten optional an) eine "PID-Datei".
Zum Beispiel bietet LibreOffice Angebote an
--pidfile={file}
, siehe die Dokumente .Ich habe lange nach einer Java / Linux-Lösung gesucht, aber die PID lag (in meinem Fall) zur Hand.
quelle
Es gibt keine einfache Lösung. In der Vergangenheit habe ich einen anderen Prozess gestartet, um entweder den
ps
Befehl auf Unix-ähnlichen Systemen oder dentasklist
Befehl unter Windows auszuführen und dann die Ausgabe dieses Befehls auf die gewünschte PID zu analysieren. In Wirklichkeit habe ich diesen Code in ein separates Shell-Skript für jede Plattform eingefügt, die gerade die PID zurückgegeben hat, damit ich das Java-Teil so plattformunabhängig wie möglich halten kann. Dies funktioniert nicht gut für kurzlebige Aufgaben, aber das war für mich kein Problem.quelle
Das jnr-process- Projekt bietet diese Funktion.
Es ist Teil der von jruby verwendeten nativen Java-Laufzeit und kann als Prototyp für ein zukünftiges Java-FFI betrachtet werden
quelle
Ich glaube, der einzige tragbare Weg, dies zu tun, besteht darin, einen (untergeordneten) Prozess über einen anderen (übergeordneten) Java-Prozess auszuführen, der mich über die tatsächliche PID des übergeordneten Prozesses informiert. Der untergeordnete Prozess kann alles sein.
Der Code dieses Wrappers lautet
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(); } }
Um es zu verwenden, erstellen Sie eine JAR-Datei mit nur dieser und rufen Sie sie mit folgenden Befehlsargumenten auf:
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...);
quelle
Wenn die Portabilität keine Rolle spielt und Sie die PID nur unter Windows ohne großen Aufwand abrufen möchten, während Sie Code verwenden, der getestet wurde und bekanntermaßen auf allen modernen Windows-Versionen funktioniert, können Sie die Winp- Bibliothek von kohsuke verwenden. Es ist auch in Maven Central für den einfachen Verbrauch erhältlich.
Process process = //...; WinProcess wp = new WinProcess(process); int pid = wp.getPid();
quelle
Es gibt eine Open-Source-Bibliothek mit einer solchen Funktion und plattformübergreifenden Implementierungen: https://github.com/OpenHFT/Java-Thread-Affinity
Es mag übertrieben sein, nur um die PID zu erhalten, aber wenn Sie andere Dinge wie CPU- und Thread-ID und insbesondere Thread-Affinität wünschen, kann es für Sie angemessen sein.
Um die PID des aktuellen Threads zu erhalten, rufen Sie einfach auf
Affinity.getAffinityImpl().getProcessId()
.Dies wird mit JNA implementiert (siehe Antwort von arcsin).
quelle
Eine Lösung besteht darin, die eigenwilligen Tools zu verwenden, die die Plattform bietet:
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; }
Haftungsausschluss: Nicht getestet, aber Sie bekommen die Idee:
ps
an, um die Prozesse aufzulisten,ps
ebenfalls auf.quelle
Für GNU / Linux- und MacOS-Systeme (oder allgemein UNIX-ähnliche Systeme) habe ich die folgende Methode verwendet, die einwandfrei funktioniert:
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; }
quelle
Unter Verwendung von JNA Unterstützung alter und neuer JVM zum Abrufen der Prozess-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; }
quelle