it-swarm.com.ru

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

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

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

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

63
raf

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

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

Runtime.exec(...)

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

Java.lang.Process

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

24
Amir Afghani

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

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

В Windows:

Runtime.exec(..)

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

Оба имеют приватное поле «ручка».

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

24
Shamit Verma

В системе 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;
  }
19
LRBH10

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

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

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

11
arcsin

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

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

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

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

Ниже приведена реализация вышеуказанной идеи с использованием JavaSysMon library. Это

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;
        }
    }
}
8
Martin Modrák

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

3
Jared Rummler

Я использовал непереносимый подход для извлечения 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
}
2
Jim Tough

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

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

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

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

1
Ondra Žižka

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

0
Stewart Murrie

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

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

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

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

0
the8472

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

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.
0
Ondra Žižka

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

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

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

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

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

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

0
Luciano