Как получить 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(...) 

возвращает объект типа

 java.lang.Process 

Класс Process является абстрактным, и вы возвращаетесь к некоторому подclassу Process, который предназначен для вашей операционной системы. Например, на компьютерах 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 9 class Process имеет новый метод long pid() , поэтому он так же прост, как

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

Я думаю, что я нашел решение, которое выглядит довольно пуленепробиваемым, работая на большинстве платформ. Вот идея:

  1. Создайте мьютекс в формате JVM, который вы приобретете, прежде чем создавать новый процесс / убить процесс
  2. Использовать зависящий от платформы код для получения списка дочерних процессов + pids вашего процесса JVM
  3. Создание нового процесса
  4. Приобретите новый список дочерних процессов + pids и сравните с предыдущим списком. Новым является ваш парень.

Поскольку вы проверяете только на дочерние процессы, вы не можете быть допущены каким-либо другим процессом на той же машине. Мьютекс в формате JVM, который позволяет вам быть уверенным, что новый процесс является правильным.

Чтение списка дочерних процессов проще, чем получение PID из объектов процесса, потому что он не требует вызовов WIN API в windowsх, и, что более важно, это уже сделано в нескольких библиотеках.

Ниже приведена реализация вышеупомянутой идеи с использованием библиотеки 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 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; } } } 

Включите 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; } 

Я использовал непереносимый подход для извлечения идентификатора UNIX из объекта Process который очень прост.

ШАГ 1: Используйте некоторые вызовы API Reflection для определения classа реализации Process на целевом сервере JRE (помните, что Process является абстрактным classом). Если ваша реализация UNIX похожа на мою, вы увидите class реализации с именем 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: на основе classа реализации и имени поля, полученного из журнала Reflection, напишите некоторый код, чтобы раздобыть class реализации Process и извлечь из него PID с помощью Reflection API. Код ниже работает для меня по моему вкусу 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 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 } 

В моем тестировании все classы 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 .

Это не общий ответ.

Тем не менее: некоторые программы, особенно сервисы и длительные программы, создают (или предлагают создать, по желанию) «файл pid».

Например, LibreOffice предлагает --pidfile={file} , см. Документы .

Я искал достаточно времени для решения Java / Linux, но PID был (в моем случае) под рукой.

Не существует простого решения. То, как я это делал в прошлом, – это запустить другой процесс для запуска команды ps в Unix-подобных системах или команды tasklist в Windows, а затем проанализировать вывод этой команды для PID, который я хочу. В действительности, я закончил тем, что превратил этот код в отдельный сценарий оболочки для каждой платформы, которая только что вернула PID, чтобы я мог сохранить кусок Java как можно более независимую от платформы. Это не работает для краткосрочных задач, но это не проблема для меня.

проект jnr-process предоставляет эту возможность.

Он является частью собственной среды исполнения java, используемой jruby и может считаться прототипом будущего 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(); } } 

Чтобы использовать его, создайте файл 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...); 

Если переносимость не вызывает беспокойства, и вы просто хотите получить pid на Windows без особых хлопот при использовании кода, который проверен и, как известно, работает во всех современных версиях Windows, вы можете использовать библиотеку winp kohsuke. Он также доступен на Maven Central для легкого потребления.

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

Существует библиотека с открытым исходным кодом, которая имеет такую ​​функцию, и имеет кросс-платформенную реализацию: https://github.com/OpenHFT/Java-Thread-Affinity

Это может быть излишним, чтобы получить PID, но если вам нужны другие вещи, такие как CPU и идентификатор streamа, и, в частности, привязка к streamу, он может быть вам адекватен.

Чтобы получить PID текущего streamа, просто вызовите Affinity.getAffinityImpl().getProcessId() .

Это реализовано с использованием JNA (см. Ответ arcsin).

Одним из решений является использование уникальных инструментов, которые предлагает платформа:

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

Отказ от ответственности: не проверено, но вы получаете идею:

  • Вызовите ps чтобы перечислить процессы,
  • Найдите свой, потому что знаете команду, с которой вы ее запустили.
  • Если есть несколько процессов с одной и той же командой, вы можете:
    • Добавьте еще один фиктивный аргумент для их дифференциации
    • Положитесь на увеличивающийся PID (не очень безопасный, а не параллельный)
    • Проверьте время создания процесса (может быть слишком грубым, чтобы действительно дифференцировать, а также не параллельно)
    • Добавьте определенную переменную среды и перечислите ее с помощью ps .

Вы можете извлечь pid из компонента времени выполнения управления, как в:

 ManagementFactory.getRuntimeMXBean().getName() 

Он состоит из pid @ hostname, поэтому вы можете легко получить pid с помощью простого регулярного выражения.

Для информации о времени выполнения (свободная память, загрузка, ansible cpus и т. Д.) Вы можете использовать:

 Runtime.getRuntime() 
Давайте будем гением компьютера.