Подтвердить что ты не робот

Как получить PID процесса, который я только начал в java-программе?

Я начал процесс со следующим кодом

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

Теперь мне нужно знать процесс pid, который я только что начал.

4b9b3361

Ответ 1

Пока нет публичного API. см. Sun Ошибка 4244896, Sun Ошибка 4250622

В качестве обходного пути:

Runtime.exec(...)

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

java.lang.Process

Класс Process является абстрактным, и вы возвращаетесь к некоторому подклассу процесса, который предназначен для вашей операционной системы. Например, на Mac, он возвращает java.lang.UnixProcess, у которого есть частное поле с именем pid. Используя Reflection, вы можете легко получить значение этого поля. Это, по общему признанию, хак, но это может помочь. Для чего вам нужен pid?

Ответ 2

На этой странице есть HOWTO:

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

В Windows:

Runtime.exec(..)

Возвращает экземпляр "java.lang.Win32Process" ) ИЛИ "java.lang.ProcessImpl"

Оба имеют частный дескриптор поля.

Это дескриптор ОС для процесса. Вам придется использовать этот + Win32 API для запроса PID. На этой странице есть сведения о том, как это сделать.

Ответ 3

Поскольку класс Java 9 Process имеет новый метод long pid(), поэтому он так же прост, как

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

Ответ 4

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

Ответ 5

Включите в свою библиотеку 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;
    }

Вы также можете загрузить JNA из здесь и JNA Platform из здесь.

Ответ 6

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

  • Создайте мьютекс в формате JVM, который вы приобретете, прежде чем создавать новый процесс/убить процесс.
  • Использовать зависящий от платформы код для получения списка дочерних процессов + pids вашего процесса JVM
  • Создание нового процесса
  • Приобретите новый список дочерних процессов + 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 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;
        }
    }
}

Ответ 7

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

Ответ 8

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

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

Ответ 9

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

Однако: Некоторые программы, особенно сервисы и длительные программы, создают (или предлагают создать, необязательно) "файл pid".

Например, LibreOffice предлагает --pidfile={file}, см. docs.

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

Ответ 10

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

Ответ 11

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

Это часть встроенной среды java, используемой jruby и может считаться прототипом будущего java-FFI

Ответ 12

Я считаю, что единственный переносимый способ сделать это - запустить (дочерний) процесс через другой (родительский) 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...);

Ответ 13

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

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

Ответ 14

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

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

Чтобы получить текущий идентификатор потока, просто вызовите Affinity.getAffinityImpl().getProcessId().

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

Ответ 15

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

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

Ответ 16

Для 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;
}

Ответ 17

Использование JNA, поддержка старой и новой JVM для получения идентификатора процесса

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

Ответ 18

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

ManagementFactory.getRuntimeMXBean().getName()

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

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

Runtime.getRuntime()