【问题标题】:Issues capturing process IDs Java捕获进程 ID Java 的问题
【发布时间】:2014-10-27 04:45:14
【问题描述】:

我最近问了一个question,它帮助我了解了 C 程序在 Java 方面的作用。我现在的目标是对两个新的子进程执行类似 C 的 fork,同时跟踪父进程 ID 和子进程 ID。但是,我不确定这是否可行。

在这里,我尝试启动进程 P 并运行两个子进程(我认为它们是 InputStreamReaders isr 和 wasr )。我已经得到了进程 P 的 PID。但是,我卡住的地方是 InputStreamReader 并不是一个真正的进程,所以我无法获得进程 ID。

我会像在另一篇文章中提到的那样,这是一个家庭作业,它提供 C 代码指令但敦促 Java 代码响应。最终目标是能够打印“父进程 ID 的子进程 ID 已开始”和“子进程 ID 已终止”——这就是我跟踪每个人的 ID 很重要的原因。

我找到了另一个post,但我不知道如何适应它。

更新:在另一个 SO 用户的帮助下,我意识到我可能以错误的方式处理问题。这里,父进程是java进程,子进程是native进程。下面更新了代码。

原始代码

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

更新的代码 使用这些tips 和另一个 SO 用户,我发现了如何捕获 Java 进程 ID。然而,每个进程的开始信号仍然存在问题。不过,这可能是另一个问题。

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

【问题讨论】:

  • 您可以使用 Reader 和 Writer 读写本机进程的 stdout 和 stdin。因此,您可以在 Java 代码中读取“ls”的输出。您可以从进程 p 中获得的 PID,这在您的示例中是通过反射完成的。这当然比我的回答要简单得多。
  • @TorstenRömer 如果我使用 Reader 和 Writer,我能获得 ID 吗?我在获取父进程 P 的 PID 时没有问题。这是为子“进程”(当前是 InputStreamReader)找到一种 ID 的想法。有人告诉我这个 Java 对象没有 ID,因此我的难题是。
  • 我想我真的误会了。 InputStreamReader 只是一个对象,实际上没有任何 ID,只是一些伪内存地址,如 java.io.BufferedReader@55f96302,与它正在读取的本机进程无关。
  • 我仍然不明白的是,为什么您要在嵌套循环中两次尝试从 /bin/ls 读取标准输出?
  • 这两个InputStreamReaders 不是子进程,它们都只是从同一个本机(子)进程读取标准输出的读者。其中一个是多余的,你永远不会使用超过一个。

标签: java


【解决方案1】:

关于您的更新:获取本机子进程 PID 的方式也应该适用于 OSX。

由于您的任务是从 Java 启动两个子进程(对吗?),您可以使用 Runtime.getRuntime().exec() 启动两个本机进程,读取并打印每个进程的标准输出,并获取并打印每个进程的 PID。

您可以选择在主线程中运行两个进程或在单独的线程中启动每个进程,基本上就像在您的示例中所做的那样。

【讨论】:

  • 但是 Java 进程 ID 呢?我确实意识到子进程(PID)适用于 unix。它现在正在获取我找不到的父 java 进程的 ID。
  • 获取 Java 进程 PID 的一种不太复杂的方法:final String runtimeName = ManagementFactory.getRuntimeMXBean().getName(); final String jvmPid = runtimeName.split("@")[0]; 请注意,这可能仅适用于 Oracles JVM。
  • 在您更新的代码中,您仍然只启动一个 one 子进程,并且您正在从中读取两次。
  • 您获取 id 的方式效果很好。我还有一个小问题,但我正在考虑问另一个问题,因为它现在与原始问题无关。如果你愿意的话,一定要看看。在您提供的两个答案中,我相信我会接受当前的答案。
【解决方案2】:

我不确定我是否完全理解您想要实现的目标,但也许这对您有所帮助。它是一个很小的 ​​C 程序,是一种包装器,您可以使用它来启动本机进程并从 Java 代码中获取其 PID:

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

假设上面的代码被编译并且可执行文件被复制到/usr/local/bin/jpwrapper,它可以被Java用来运行一个像ls -l /dev这样的本地进程,在进程启动时打印出PID,打印它的stdout,然后再次打印PID它终止了:

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

}

但是,就像我在评论中已经承认的那样,从 UnixProcess 获取 PID 更容易(但也很麻烦)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-25
    • 1970-01-01
    • 2011-09-23
    • 2011-05-31
    相关资源
    最近更新 更多