While trying the new ProcessHandle
API in Java 9 on a simple "Dockerized" Java program, I found a difference in terms of behavior when it comes to retrieving process IDs of running processes. Specifically when calling the method ProcessHandle.pid()
, the resulting PID on Docker is different than the native ID shown on the host, although the docs say that the method "returns the native process ID of the process". In addition, there was a difference between the results of ProcessHandle.allProcesses()
.
To demonstrate, the below program does the following:
public static void main(String[] args) {
System.out.println("### Current process info ###");
ProcessHandle currentProcess = ProcessHandle.current();
printInfo(currentProcess);
System.out.println();
// Fork a child process that lasts for a few seconds
spawnProcess("jshell --startup ./sleep.txt");
printAllVisibleProcesses();
}
private static void printAllVisibleProcesses() {
System.out.println("### Visible processes info ###");
ProcessHandle.allProcesses().forEach(ProcessHandleExamples::printInfo);
System.out.println();
}
private static void spawnProcess(String command) {
System.out.println("Spawning: " + command);
try {
Runtime.getRuntime().exec(command);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void printInfo(ProcessHandle processHandle) {
ProcessHandle.Info processInfo = processHandle.info();
System.out.println("Process ID: " + processHandle.pid());
System.out.println("Process arguments: " + Arrays.toString(processInfo.arguments().orElse(new String[0])));
System.out.println("Process executable: " + processInfo.command().orElse(""));
System.out.println("Process command line: " + processInfo.commandLine().orElse(""));
System.out.println("Process start time: " + processInfo.startInstant().orElse(null));
System.out.println("Process total cputime accumulated: " + processInfo.totalCpuDuration().orElse(null));
System.out.println("Process user: " + processInfo.user().orElse(""));
}
When running the application normally (without Docker), the output is as expected; it includes the native PID of the current process, its child process and lots of other visible processes.
### Current process info ###
Process ID: 7756
Process arguments: []
Process executable: D:\Dev\Java\jdk-9\bin\java.exe
Process command line:
Process start time: 2017-10-08T12:23:46.474Z
Process total cputime accumulated: PT0.4368028S
Process user: manouti
Spawning: jshell --startup ./sleep.txt
### Visible processes info ###
... skipping some output
Process ID: 8060
Process arguments: []
Process executable: C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
Process command line:
Process start time: 2017-10-08T12:20:04.758Z
Process total cputime accumulated: PT10.4676671S
Process user: manouti
Process ID: 7756
Process arguments: []
Process executable: D:\Dev\Java\jdk-9\bin\java.exe
Process command line:
Process start time: 2017-10-08T12:23:46.474Z
Process total cputime accumulated: PT0.8268053S
Process user: manouti
Process ID: 8080
Process arguments: []
Process executable: D:\Dev\Java\jdk-9\bin\jshell.exe
Process command line:
Process start time: 2017-10-08T12:23:46.992Z
Process total cputime accumulated: PT0.0780005S
Process user: manouti
When I run on Docker (Windows 7 with Docker running on a boot2docker Linux), a much smaller subset of processes are visible, and the PIDs do not match the ones on the host.
$ docker run test/java9-processhandle-example:1.0
After running the above command, the host shows the following processes:
However, the resulting program output below shows PIDs 1 and 16, instead of 4291 and 4333. And the visible processes include the container process and the spawned process.
I'm wondering if this is expected. Since I am relatively new to Docker, if this is a limitation caused by the container, I would be glad if someone could explain it (I'm also not sure if this is reproducible on a different Docker setup, e.g. Docker on Linux or Windows Server). Otherwise, is this a limitation by the API itself when applied in containers (which does not seem to be mentioned anywere in the Javadocs)?
### Current process info ###
Process ID: 1
Process arguments: [ProcessHandleExamples]
Process executable: /usr/lib/jvm/java-9-openjdk-amd64/bin/java
Process command line: /usr/lib/jvm/java-9-openjdk-amd64/bin/java ProcessHandleExamples
Process start time: 2017-10-08T14:17:48.420Z
Process total cputime accumulated: PT0.35S
Process user: root
Spawning: jshell --startup ./sleep.txt
### Visible processes info ###
Process ID: 1
Process arguments: [ProcessHandleExamples]
Process executable: /usr/lib/jvm/java-9-openjdk-amd64/bin/java
Process command line: /usr/lib/jvm/java-9-openjdk-amd64/bin/java ProcessHandleExamples
Process start time: 2017-10-08T14:17:48.420Z
Process total cputime accumulated: PT0.6S
Process user: root
Process ID: 16
Process arguments: [--startup, ./sleep.txt]
Process executable: /usr/lib/jvm/java-9-openjdk-amd64/bin/jshell
Process command line: /usr/lib/jvm/java-9-openjdk-amd64/bin/jshell --startup ./sleep.txt
Process start time: 2017-10-08T14:17:49.070Z
Process total cputime accumulated: PT0.03S
Process user: root
This is nothing specific to Java or Java 9, it's a docker theme.
Each container has its own PID namespace and the first process that is run in the container has a PID of 1.
You can read more about this in the docker documentation, especially:
By default, all containers have the PID namespace enabled.
PID namespace provides separation of processes. The PID Namespace removes the view of the system processes, and allows process ids to be reused including pid 1.