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

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

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

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


person raf    schedule 20.01.2011    source источник
comment
Некоторым это может помочь: stackoverflow.com/questions/35842/process-id-in-java   -  person Grammin    schedule 20.01.2011
comment
Не знаю, как это сделать на Java, но если это ваш скрипт, вы можете его изменить, чтобы он выводил его pid и анализировал его из p.getInputStream ().   -  person maaartinus    schedule 20.01.2011
comment
НЕ точная копия того, кто проголосовал за закрытие. Этот вопрос связан с поиском pid самого процесса Java, а не нового процесса, порожденного процессом Java.   -  person Mark Peters    schedule 20.01.2011
comment
Зачем вообще нужен PID?   -  person Raedwald    schedule 20.01.2011
comment
Связано: http://stackoverflow.com/questions/3492850/get-pid-of-javaws-jnlp-applet-under-linux   -  person finnw    schedule 20.01.2011
comment
В соответствии с рекомендациями ‹a href=robaustin.wikidot.com / ›, попробовал java.lang.management.ManagementFactory System.out.println (ManagementFactory.getRuntimeMXBean (). getName ()); возвращает строку, подобную 11639 @ myMachineName, где 11639 - это идентификатор текущего процесса. Проверено на Windows XP и Linux версии 2.6.16.46.   -  person user799188    schedule 23.06.2011
comment
Это возвращает PID работающей виртуальной машины хоста, исходный вопрос заключался в том, чтобы узнать, каким будет PID дочернего процесса, запущенного из запущенного приложения Java. Этот вопрос решает эту проблему: stackoverflow.com/questions/5284139/   -  person Riyad Kalla    schedule 16.08.2011
comment
Возможно, что Java 9 предоставит для этого стандартный API: openjdk.java.net/jeps/102   -  person Raedwald    schedule 13.05.2015
comment
Возможный дубликат Как программа на Java может получить свой собственный процесс ID?   -  person RobEarl    schedule 24.02.2017
comment
@RobEarl нет, это не так, это для процесса, получающего собственный PID, это для обработки, получающей PID Process, который он запустил   -  person Novaterata    schedule 28.11.2017


Ответы (17)


Поскольку Java 9 класс Process имеет новый метод _ 2_, так что это так же просто, как

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

Для этого пока нет общедоступного API. см. Sun Ошибка 4244896, Sun Ошибка 4250622

Как обходной путь:

Runtime.exec(...)

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

java.lang.Process

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

person Amir Afghani    schedule 20.01.2011
comment
В Windows он возвращает java.lang.ProcessImpl, который не имеет понятия PID. К сожалению, ваше решение не является кроссплатформенным. - person Espinosa; 29.06.2013
comment
Есть еще один неплохой пример, который обеспечивает разумную кроссплатформенную поддержку по адресу: github.com/flapdoodle-oss/de.flapdoodle.embed.process/blob/ - person satyagraha; 09.12.2014
comment
Я не знаю о OP, но мне нужно иметь возможность убить и, возможно, перезапустить процесс в какой-то момент. - person Nate Lockwood; 21.07.2016
comment
Обратите внимание, что этот взлом больше не будет работать на Java 9 в Linux - не уверен, что это за Mac. - person Oliver Gondža; 07.04.2017
comment
Для пользователей Java 9+ предлагается решение только для JDK: stackoverflow.com/a/42104000/363573 < / а> - person Stephan; 03.07.2017

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

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

В Windows:

Runtime.exec(..)

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

У обоих есть личное поле "handle".

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

person Shamit Verma    schedule 07.04.2011
comment
См. Ответ arcsin о том, как это сделать с помощью JNA. - person Panayotis; 08.08.2017
comment
Обратитесь к ответам LRBH10 и czerny. Это лучшие подходы в 2019 году. - person Shamit Verma; 14.05.2019

В системе 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;
  }
person LRBH10    schedule 16.10.2015
comment
@idelvall, что вы хотите сделать здесь поточно-ориентированным? Pid является окончательным, чтение его значения безопасно. - person Miha_x64; 29.05.2018
comment
@ Miha_x64 Поле Я думаю, я сказал, что, думая, что getDeclaredField() всегда возвращает один и тот же экземпляр (это не так). Так что я больше не могу держать это заявление - person idelvall; 30.05.2018

Включите jna (как JNA, так и платформу JNA) в свою библиотеку. и используйте эту функцию:

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 с здесь.

person arcsin    schedule 15.04.2017
comment
Не могли бы вы объяснить, что делает приведенный выше код? Так поймут все будущие посетители. Спасибо - person Drew Szurko; 15.04.2017
comment
В ответе Шамита Верма отмечается, почему это нужно делать именно так. Windows дает вам не PID, а дескриптор, поэтому вам нужно преобразовать дескриптор в PID. - person Panayotis; 08.08.2017

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

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

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

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

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

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

person Jared Rummler    schedule 01.04.2015
comment
Это не относится к java.lang.ProcessImpl. Например, у меня это не работает в Windows. - person Oliver Gondža; 08.02.2016

Я использовал непереносимый подход для получения UNIX PID из объекта 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, напишите код, чтобы украсть класс реализации Process и получить от него PID с помощью API Reflection. Приведенный ниже код работает для меня с моей разновидностью 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
}
person Jim Tough    schedule 04.11.2014

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

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

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

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

person Ondra Žižka    schedule 12.12.2017
comment
Это гораздо лучшее решение, чем решение для Java / Linux. Спасибо за идею! - person Loganathan; 07.02.2018

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

person Stewart Murrie    schedule 20.01.2011

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

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

person the8472    schedule 19.08.2015

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

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

Process process = //...;
WinProcess wp = new WinProcess(process);
int pid = wp.getPid();
person allquixotic    schedule 07.09.2017

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

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

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

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

person Luciano    schedule 02.11.2017

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

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, чтобы просмотреть список процессов,
  • Найдите свой, потому что вы знаете команду, с которой его запустили.
  • If there are multiple processes with the same command, you can:
    • Add another dummy argument to differentiate them
    • Положитесь на увеличивающийся PID (не совсем безопасно, не одновременно)
    • Проверьте время создания процесса (может быть слишком грубым, чтобы действительно различать, также не одновременным)
    • Добавьте конкретную переменную среды и укажите ее вместе с ps.
person Ondra Žižka    schedule 12.12.2017

Для систем 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;
}
person csonuryilmaz    schedule 31.10.2018

Использование 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;
}
person Raj    schedule 23.09.2019