运行 ps 时是否存在竞争条件的风险?

运行 ps 时是否存在竞争条件的风险?

我正在启动一个长时间运行的进程(在 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 程序中。

相关内容