88

I've started a process with following code

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

Now I need to know the process's pid that I've just started.

Dennis Meng
  • 5,109
  • 14
  • 33
  • 36
raf
  • 1,121
  • 3
  • 13
  • 14
  • 7
    This may help some: http://stackoverflow.com/questions/35842/process-id-in-java – Grammin Jan 20 '11 at 17:33
  • 3
    No idea how to do it in Java, but in case it's your script you may modify it, so it outputs it's pid and parse it from p.getInputStream(). – maaartinus Jan 20 '11 at 17:37
  • 6
    NOT an "exact duplicate" to the one who voted to close. That question has to do with finding the pid of the Java process itself, not a new process that the Java process spawned. – Mark Peters Jan 20 '11 at 17:37
  • What do you need the PID for anyway? – Raedwald Jan 20 '11 at 17:59
  • Related: http://stackoverflow.com/questions/3492850/get-pid-of-javaws-jnlp-applet-under-linux – finnw Jan 20 '11 at 19:26
  • 2
    It is possible that Java 9 will provide a standard API for doing this: http://openjdk.java.net/jeps/102 – Raedwald May 13 '15 at 09:31
  • Possible duplicate of [How can a Java program get its own process ID?](http://stackoverflow.com/questions/35842/how-can-a-java-program-get-its-own-process-id) – RobEarl Feb 24 '17 at 10:31
  • As recommended here, I tried java.lang.management.ManagementFactory System.out.println(ManagementFactory.getRuntimeMXBean().getName()); returns a string similar to 11639@myMachineName where 11639 is the ID of the current process. Tested on Windows XP and Linux version 2.6.16.46. – user799188 Jun 23 '11 at 03:52
  • 1
    This returns the PID of the *running* host VM, the original question was asking what the PID of the child process started from the running Java app would be. This question addresses it: http://stackoverflow.com/questions/5284139/how-do-i-find-the-process-id-pid-of-a-process-started-in-java – Riyad Kalla Aug 16 '11 at 01:42
  • @RobEarl no it's not, that is for a process getting it's own PID, this is for a processing getting the PID of a `Process` it started – Novaterata Nov 28 '17 at 19:31

18 Answers18

51

Since Java 9 class Process has new method long pid(), so it is as simple as

ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "path");
try {
    Process p = pb.start();
    long pid = p.pid();      
} catch (IOException ex) {
    // ...
}
Inv3r53
  • 2,929
  • 3
  • 25
  • 37
czerny
  • 15,090
  • 14
  • 68
  • 96
31

There is no public API for this yet. see Sun Bug 4244896, Sun Bug 4250622

As a workaround:

Runtime.exec(...)

returns an Object of type

java.lang.Process

The Process class is abstract, and what you get back is some subclass of Process which is designed for your operating system. For example on Macs, it returns java.lang.UnixProcess which has a private field called pid. Using Reflection you can easily get the value of this field. This is admittedly a hack, but it might help. What do you need the PID for anyway?

Steel Brain
  • 4,321
  • 28
  • 38
Amir Afghani
  • 37,814
  • 16
  • 84
  • 124
  • 14
    On Windows it returns java.lang.ProcessImpl which has no notion of PID. Your solution is not cross platform unfortunately. – Espinosa Jun 28 '13 at 20:55
  • 1
    There's another quite good example which provides reasonable cross-platform support at:https://github.com/flapdoodle-oss/de.flapdoodle.embed.process/blob/master/src/main/java/de/flapdoodle/embed/process/runtime/Processes.java – satyagraha Dec 09 '14 at 08:46
  • 1
    I don't know about the OP but I need to be able to kill and possibly restart the process at some point. – Nate Lockwood Jul 20 '16 at 23:20
  • Note that the hack will no longer work on Java 9 on linux - not sure about Mac. – Oliver Gondža Apr 07 '17 at 12:04
  • For **Java 9+** users, here is a JDK only solution: https://stackoverflow.com/a/42104000/363573 – Stephan Jul 03 '17 at 09:12
23

In Unix System (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;
  }
LRBH10
  • 279
  • 2
  • 6
  • 2
    @idelvall what do you want to be thread-safe here? Pid is final, reading its value is safe. – Miha_x64 May 29 '18 at 09:27
  • @Miha_x64 Field I guess I said that thinking that `getDeclaredField()` returns always the same instance (it does not). So i can't hold that statement any more – idelvall May 30 '18 at 07:50
  • 1
    in my environment, the `pid` field in `java.lang.UNIXProcess` is `int`, not `long` – BLuFeNiX Jan 05 '23 at 21:36
23

This page has the HOWTO:

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

On Windows:

Runtime.exec(..)

Returns an instance of "java.lang.Win32Process") OR "java.lang.ProcessImpl"

Both have a private field "handle".

This is an OS handle for the process. You will have to use this + Win32 API to query PID. That page has details on how to do that.

Shamit Verma
  • 3,839
  • 23
  • 22
14

Include jna (both "JNA" and "JNA Platform") in your library and use this function:

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

You can also download JNA from here and JNA Platform from here.

arcsin
  • 441
  • 4
  • 10
  • Could you please explain what the above code does? That way any future visitors will understand. Thanks – Drew Szurko Apr 15 '17 at 15:10
  • 1
    The answer of Shamit Verma notes why it needs to be done this way. Windows doesn't give you a PID but a handle, so you need to transform the handle to PID. – Panayotis Aug 08 '17 at 09:54
  • Unfortunately the windows side doesn't work with JDK 9+ anymore, so a little tricky still https://github.com/jruby/jruby/issues/7515 – rogerdpack Dec 30 '22 at 18:21
8

I think I have found out a solution, that looks quite bulletproof while working on most platforms. Here is the idea:

  1. Create a JVM-wide mutex that you acquire before spawning new process/killing a process
  2. Use platform-dependent code to acquire list of child processes + pids of your JVM process
  3. Spawn new process
  4. Acquire new list of child processes + pids and compare with the previous list. The one that is new is your guy.

Since you check only for child processes, you cannot be wronged by some other process in the same machine. JVM-wide mutex than allows you to be sure, that the new process is the correct one.

Reading child process list is simpler than getting PID from process objects, because it does not require WIN API calls on windows, and, more importantly, it has been done already in several libs.

Below is an implementation of the above idea using JavaSysMon library. It

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;
        }
    }
}
glenneroo
  • 1,908
  • 5
  • 30
  • 49
Martin Modrák
  • 746
  • 8
  • 17
5

In my testing all IMPL classes had the field "pid". This has worked for me:

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

Just make sure the returned value is not -1. If it is, then parse the output of ps.

Jared Rummler
  • 37,824
  • 19
  • 133
  • 148
3

I used a non-portable approach to retrieve the UNIX PID from the Process object that is very simple to follow.

STEP 1: Use some Reflection API calls to identify the Process implementation class on the target server JRE (remember that Process is an abstract class). If your UNIX implementation is like mine, you will see an implementation class that has a property named pid that contains the PID of the process. Here is the logging code that I used.

    //--------------------------------------------------------------------
    // 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());
    }
    //--------------------------------------------------------------------

STEP 2: Based on the implementation class and field name that you obtained from the Reflection logging, write some code to pickpocket the Process implementation class and retrieve the PID from it using the Reflection API. The code below works for me on my flavour of UNIX. You may have to adjust the EXPECTED_IMPL_CLASS_NAME and EXPECTED_PID_FIELD_NAME constants to make it work for you.

/**
 * 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
}
Jim Tough
  • 14,843
  • 23
  • 75
  • 96
1

This is not a generic answer.

However: Some programs, especially services and long-running programs, create (or offer to create, optionally) a "pid file".

For instance, LibreOffice offers --pidfile={file}, see the docs.

I was looking for quite some time for a Java/Linux solution but the PID was (in my case) lying at hand.

Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
1

Using JNA, supporting old and new JVM to get process id

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;
}
Raj
  • 1,156
  • 11
  • 15
0

the jnr-process project provides this capability.

It is part of the java native runtime used by jruby and can be considered a prototype for a future java-FFI

the8472
  • 40,999
  • 5
  • 70
  • 122
0

I believe the only portable way to do this, is to run a (child) process through another (parent) Java process, which will inform me the actual PID of the parent process. The child process could be anything.

The code of this wrapper is

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

To use it, create a jar file with just this one, and call it with command arguments this:

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...);
Panayotis
  • 1,792
  • 23
  • 32
0

If portability is not a concern, and you just want to get the pid on Windows without a lot of hassle while using code that is tested and known to work on all modern versions of Windows, you can use kohsuke's winp library. It is also available on Maven Central for easy consumption.

Process process = //...;
WinProcess wp = new WinProcess(process);
int pid = wp.getPid();
allquixotic
  • 1,481
  • 2
  • 18
  • 37
0

There is an open-source library that has such a function, and it has cross-platform implementations: https://github.com/OpenHFT/Java-Thread-Affinity

It may be overkill just to get the PID, but if you want other things like CPU and thread id, and specifically thread affinity, it may be adequate for you.

To get the current thread's PID, just call Affinity.getAffinityImpl().getProcessId().

This is implemented using JNA (see arcsin's answer).

Luciano
  • 2,388
  • 1
  • 22
  • 33
0

There isn't a simple solution. The way I've done it in the past is to start another process to run either the ps command on Unix-like systems, or the tasklist command on Windows, and then parse the output of that command for the PID I want. In reality, I ended up putting that code into a separate shell script for each platform which just returned the PID, so that I could keep the Java piece as platform independent as possible. This doesn't work well for short-lived tasks, but that wasn't an issue for me.

Stewart Murrie
  • 1,319
  • 7
  • 12
0

One solution is to use the idiosyncratic tools the platform offers:

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

Disclaimer: Not tested, but you get the idea:

  • Call ps to list the processes,
  • Find your one because you know the command you launched it with.
  • If there are multiple processes with the same command, you can:
    • Add another dummy argument to differentiate them
    • Rely on the increasing PID (not really safe, not concurrent)
    • Check the time of process creation (could be too coarse to really differentiate, also not concurrent)
    • Add a specific environment variable and list it with ps too.
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
0

For GNU/Linux & MacOS (or generally UNIX like) systems, I've used below method which works fine:

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;
}
csonuryilmaz
  • 1,715
  • 24
  • 24
0

I made a quick and dirty solution for those who are still stuck with Java 8

public long showID(Process process) {
    return Long.parseLong(process.toString().split(", ")[0].replace("Process[pid=", ""));
}

I hope you find this snippet useful.

Gustavo
  • 1
  • 2