0

I recently asked a question that helped me understand what a C program is doing in terms of Java. My goal is now to perform a C-like fork of two new child processes, while keeping track of parent process IDs and child process Ids. I am unsure, however, if this is possible.

Here, I've attempted to initiate process P and run two child processes (which I believed to be the InputStreamReaders isr and wasr ). I've gotten the PID of process P. But, where I'm stuck is the fact that InputStreamReader is not really a process, so I can't get the process ID.

I'll mention like I did in the other post that this is a homework assignment which provides C code instruction but urging Java code responses. The end goal is to be able to print "Child process ID of Parent process ID has begun" and "Child process ID has terminated" -- this is why it's important that I keep track of everyone's ID.

I found this other post, but I'm not sure how to adapt it.

UPDATE : With help of another SO user, I've realized that i may be approaching the problem in the wrong way. Here, parent process is the java process and child process is the native process. Updated code below.

Original Code

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;


class processesorigin {
public static void main(String[] args) {
    try {
        Process p = Runtime.getRuntime().exec("/bin/ls");
        final InputStream is = p.getInputStream();
        final InputStream was = p.getInputStream();
        Thread t = new Thread(new Runnable() {
            public void run() {
                InputStreamReader isr = new InputStreamReader(is);
                InputStreamReader wasr = new InputStreamReader(was);
                int ch;
                try {
                    while ((ch = isr.read()) != -1) {
                        System.out.print((char) ch);
                    }
                    while ((ch = wasr.read()) != -1) {
                         System.out.print((char) ch);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
        p.waitFor();
        t.join();
        int pid = 0;
        if(p.getClass().getName().equals("java.lang.UNIXProcess")) {
            try {
                Field f = p.getClass().getDeclaredField("pid");
                f.setAccessible(true);
                pid = f.getInt(p);
                }
            catch (Throwable e) {
                }
            }
        System.out.println("Process " + pid + " terminates.");
    } catch (Exception e) {
        e.printStackTrace();
    }
}
}

UPDATED CODE Using these tips and another SO user, I have found how to capture the Java process ID. There is however, still an issue with signaling of the start of each process. This might be another question though.

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import sun.management.VMManagement;
import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;


class processesorigin {
  public static void main(String[] args) {
    try {
      Process p = Runtime.getRuntime().exec("/bin/ls");
      Process q = Runtime.getRuntime().exec("/bin/ls");
      final InputStream is = p.getInputStream();
      final InputStream was = q.getInputStream();

      /*get PID of Java process*/
      final String runtimeName = ManagementFactory.getRuntimeMXBean().getName();
      final String jvmPid = runtimeName.split("@")[0];

      int pid = 0;
      int qid = 0;
      if(p.getClass().getName().equals("java.lang.UNIXProcess")) {
        /* get the PID of child processes : native ls command*/
        try {
          Field f = p.getClass().getDeclaredField("pid");
          Field g = q.getClass().getDeclaredField("pid");
          f.setAccessible(true);
          g.setAccessible(true);
          pid = f.getInt(p);
          qid = g.getInt(q);
        }
        catch (Throwable e) {
        }
      }

      final int pidCopy = pid;
      final int qidCopy = qid;
      Thread t = new Thread(new Runnable() {
        public void run() {
          InputStreamReader isr = new InputStreamReader(is);
          InputStreamReader wasr = new InputStreamReader(was);
          int ch;
          try {
            System.out.print("Child process " + pidCopy + " of Parent process " + jvmPid + " begun.");
            while ((ch = isr.read()) != -1) {
              System.out.print((char) ch);
            }
            System.out.print("Child process " + qidCopy + " of Parent process " + jvmPid + " begun.");
            while ((ch = wasr.read()) != -1) {
              System.out.print((char) ch);
            }                 
          } catch (IOException e) {
            e.printStackTrace();
          }
        }
      });
      t.start();
      p.waitFor();
      q.waitFor();
      t.join();
      System.out.println("Child process " + pidCopy + " terminated.");
      System.out.println("Child process " + qidCopy + " terminated.");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
Community
  • 1
  • 1
user25976
  • 1,005
  • 4
  • 18
  • 39
  • You can use a Reader and Writer read and write from/to the native processe's stdout and stdin. So you could read the output of 'ls' in your Java code. The PID you can get from the process p, which is done via reflection in your example. This is of course a much easier way than my answer. – Torsten Römer Oct 25 '14 at 21:38
  • @TorstenRömer If i use a Reader and Writer, would I be able to get an ID, though? I didn't have a problem getting a PID of parent process P. It's the idea of finding a sort of ID for the child "process", which is currently the InputStreamReader. I've been told that this Java object doesn't have an ID, thus my conundrum. – user25976 Oct 25 '14 at 22:24
  • I think I really misunderstood. The InputStreamReader is just an object and indeed does not have any ID, just some pseudo memory address like `java.io.BufferedReader@55f96302` which has no relation to the native process it is reading from. – Torsten Römer Oct 25 '14 at 22:32
  • What I still don't understand is why you are trying to read stdout from `/bin/ls` twice in a nested loop? – Torsten Römer Oct 25 '14 at 22:46
  • @TorstenRömer I mentioned this is for homework -- i was asked to fork two child processes from one parent process. What i believed I was doing was forking first child process isr and second child process wasr from parent process p. Maybe it's me who is misunderstanding though... – user25976 Oct 25 '14 at 22:50
  • @TorstenRömer Ahh--okay, and I see now an error. It shouldn't be a nested while loop. I'm working on fixing it and i'll edit. – user25976 Oct 25 '14 at 22:52
  • For me the parent process is the Java process and the child process is the native process `/bin/ls` you start with `Runtime.getRuntime().exec("/bin/ls");` The PID you print is the ID of it. The InputStreamReader is just a stream connected to the native processes' stdout, allowing you to read the output of `/bin/ls` in your example. – Torsten Römer Oct 25 '14 at 22:54
  • 1
    The two `InputStreamReader`s are not child processes, they are both just readers reading stdout from the same native (child) process. One of them is redundant, you would never use more than one. – Torsten Römer Oct 25 '14 at 23:06

2 Answers2

0

I am not sure if I fully understand what you want to achieve but maybe this helps you anyway. It is a tiny C program, a kind of wrapper that you can use to start a native process and obtain its PID from Java code:

#include <unistd.h>
#include <stdio.h>

/*
 * Purpose of this "program" is to write its PID to stdout so that
 * it can be read by a Java program which then can shutdown this
 * process gently by sending for example SIGINT to it.
 * After printig its PID the process executes the program given
 * as argument.
 */
int main(int argc, char *argv[]) {
  if(argc < 3) {
    fprintf(stderr,"Usage: %s <program> <name> [arg] ...\n", argv[0]);
    return -1;
  }

  /* Write the PID of the process to stdout */
  fprintf(stdout,"%i\n", getpid());

  /* Flush the buffers */
  fflush(NULL);

  /*
   * Execute the program (specified as file or full path) given as first
   * argument in the current process.
   */ 
  return execvp(argv[1], argv + 2);
}

Assuming above code was compiled and the executable copied to /usr/local/bin/jpwrapper, it can be used from Java to run a native process like ls -l /dev, print out the PID when the process started, print its stdout and again the PID when it terminated:

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class PID {

    public static void main(final String[] args) throws Exception {
        final Process process = Runtime.getRuntime().exec(new String[] {
                "/usr/local/bin/jpwrapper",
                "/bin/ls", "ls", "-l", "/dev"});

        String pid = null;
        try (final BufferedReader reader = new BufferedReader(
                new InputStreamReader(process.getInputStream()))) {
            // First line of output from the wrapper is the PID
            String line = reader.readLine();
            pid = line;
            System.out.println(String.format(
                    "Process with PID %s started", pid));

            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        }

        final int exitCode = process.waitFor();
        System.out.println(String.format(
                "Process with PID %s terminated with exit code %s", 
                pid, exitCode));
    }

}

But, like I already admitted in my comment, getting the PID from UnixProcess is easier (but also hacky).

Torsten Römer
  • 3,834
  • 4
  • 40
  • 53
0

Regarding your update: The way how you are getting the PID of the native child process should also work on OSX.

Since your assignment is to start two child processes from Java (right?) you can just start two native processes using Runtime.getRuntime().exec(), read and print stdout of each of them, and get and print the PID of each of them.

You can choose to run both processes in the main thread or start each process in a separate thread, basically like it is done in your example.

Torsten Römer
  • 3,834
  • 4
  • 40
  • 53
  • But what about the Java process ID? I do realize that the child process (PID) works for unix. It's now getting the ID of the parent java process that i'm not finding. – user25976 Oct 25 '14 at 23:47
  • A less complicated way to get the PID of the Java process: `final String runtimeName = ManagementFactory.getRuntimeMXBean().getName(); final String jvmPid = runtimeName.split("@")[0];` Note that this may only work with Oracles JVM. – Torsten Römer Oct 26 '14 at 10:36
  • In your updated code, you are still starting only one one child process and you are reading twice from it. – Torsten Römer Oct 26 '14 at 10:39
  • Your way of getting the id works well. I still have a small issue, but I'm considering asking another question as it's now pretty unrelated to the original question. Do take a look though if you're willing. Of the two answers you've provided, I believe I will accept this current answer. – user25976 Oct 26 '14 at 19:36