Как получить PID процесса, который я только что начал в Java-программе? - PullRequest
66 голосов
/ 20 января 2011

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

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

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

Ответы [ 16 ]

25 голосов
/ 07 апреля 2011

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

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

В Windows:

Runtime.exec(..)

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

У обоих есть приватное поле "handle".

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

24 голосов
/ 20 января 2011

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

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

Runtime.exec(...)

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

java.lang.Process

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

22 голосов
/ 08 февраля 2017

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

ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
    Process p = pb.start();
    long pid = p.pid();      
} catch (IOException ex) {
    // ...
}
19 голосов
/ 16 октября 2015

в системе 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;
  }
13 голосов
/ 15 апреля 2017

Включите 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 от здесь .

8 голосов
/ 22 марта 2012

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

  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<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;
        }
    }
}
3 голосов
/ 02 апреля 2015

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

2 голосов
/ 04 ноября 2014

Я использовал непереносимый подход для извлечения PID 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: Основываясь на классе реализации и имени поля, которые вы получили из журнала отражений, напишите некоторый код, чтобы украсть 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
}
1 голос
/ 12 декабря 2017

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

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

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

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

0 голосов
/ 01 ноября 2018

Для систем 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;
}
...