我正在启动一个长时间运行的进程(在 Java 中,偶尔相关),然后希望通过ps
.我基本上是这样做的:
Process longRunningProcess = new ProcessBuilder(...).start();
Process psProcess = new ProcessBuilder("ps").start();
psProcess.waitFor();
// extract the PID from the output of psProcess
或者在 Bash 中等效地:
$ long_running_process > /dev/null & ps
问题是,长时间运行的进程是否有可能处于某种尚未ps
报告的仍在启动的状态?或者是否有任何形式的保证,在执行时,ps
长时间运行的进程将启动到足以可见?重复运行命令出现始终包含该过程,但这显然并不能证明任何事情。
本文警告启动的命令可能存在竞争条件后 ps
已启动(例如ps | grep
),但没有提及有关之前启动的进程的任何内容ps
。
答案1
如果操作与OpenJDK相当v6-b14,PID将要ps
扫描虚拟文件系统时可用/proc
。您无法确定该过程是否真正开始(即完成任何操作);你所知道的是一个PID已经被保留和调度了(想想看,这个进程可能有死了并处于僵尸状态)。
但是,由于ps
将您锁定到 Unix 系统,因此您可以使用 Reflection 来深入研究 ProcessBuilder 返回的对象,因为它会将您引导到 UnixProcess:
final class [More ...] UNIXProcess extends Process {
private FileDescriptor stdin_fd;
private FileDescriptor stdout_fd;
private FileDescriptor stderr_fd;
private int pid; <----------------------------------------------
private int exitcode;
当然这个是一个黑客,但与产卵没有太大不同ps
- 而且肯定更快。并没有那么危险——我没有看到一个名为“pid
很快改变”的 pid 字段。
答案2
我对 Java 不太熟悉,但是当ProcessBuilder.start
返回时,它一定已经启动了一些东西。它可以在现有线程内设置一个回调,稍后执行以创建子进程,或者它可以在同一进程中启动另一个线程,最终创建子进程,或者它可以调用fork
来创建子进程。我怀疑它是否是前两者中的任何一个,因为它更难得到正确的结果;对子流程的任何引用,包括与其通信或检索其状态的机制,都必须等到该流程创建。
因此,当ProcessBuilder.start
返回时,您可以确定该进程已创建。然而,这并不意味着不存在竞争条件。如果进程在启动后不久就退出或崩溃,那么当您查看它时它可能已经死了。进程 ID 将仍然存在,直到其父进程调用其中之一wait
系统调用家族;如果进程已经死亡,只要该进程是一个进程,PID 就保持有效僵尸,即只要父母没有打电话wait
。
因此,确保进程ID有效的唯一方法是与父进程协调并确保父进程已经调用fork
并且尚未调用wait
。
答案3
我过去曾遇到过这个问题。我通过这样做解决了这个问题:
exec java WhateverTheClassNameIs -p $$
其中“-p”是一个标志,表示“我的 PID 是该标志的值”。您必须在 Javamain()
方法中编写该部分的代码。
这必须是 shell 脚本的最后一行,因为原语通过 shell 本身exec
加载。java
程序的退出状态将是 的退出状态WhateverTheClassNameIs
,而不是 shell 的退出状态,因为它实际上不再存在。这可能会导致代码有些扭曲,但我找不到更好的方法将 PID 放入 Java 程序中。