在JAVA中使用PID验证进程是否正在运行

6
我目前正在构建一个JAVA应用程序,该程序只能执行一次。因此,我目前使用锁定文件,在其中编写当前执行的PID。所以每当此应用程序启动时,它将打开文件(如果存在),并尝试检测文件中编写的PID是否正在运行。这可以防止我的应用程序在解锁文件之前崩溃。我需要它在Windows(XP、7或8)和Linux上都能工作(所有用户都在基于Debian的发行版上)。以下是一些代码,以便您更好地了解我想做什么:
//get the PID from the file
int pidValue = new FileReader(file).read();

//get the OS type
String os = System.getProperty("os.name").toLowerCase();

//Check PID depending of OS type
if( os.contains("nux") || os.contains("nix") ){
/*
 * Check PID on Linux/Unix
*/
} else if ( os.contains("win") ) {
/*
 * Check PID on Windows
 */
}

我尝试查找有关此主题的文档,但我尚未找到任何有用的信息。
非常感谢。

对于 Linux,您可以使用 ps 命令执行进程并检查 PID 的输出;而对于 Windows,则可以使用 tasklist 命令。 - PeterMmm
这些函数是否返回某种响应?我需要以编程方式确定进程是否正在运行。 - ElCapitaine
https://dev59.com/D3VD5IYBdhLWcg3wNIvc - PeterMmm
5个回答

4
以下代码确定指定pid的进程是否正在运行。它已在Windows 7和Ubuntu 13上进行了测试。在Windows上,它使用apache commons-exec来运行tasklist并基于其退出代码确定是否找到了指定的pid。通过将结果管道传递给findstr,它克服了tasklist始终返回0的事实。在Linux上,它使用ps执行相同的操作。它还抑制了子进程的标准输出日志记录。
public static boolean isProcessRunning(int pid, int timeout, TimeUnit timeunit) throws java.io.IOException {
    String line;
    if (OS.isFamilyWindows()) {
        //tasklist exit code is always 0. Parse output
        //findstr exit code 0 if found pid, 1 if it doesn't
        line = "cmd /c \"tasklist /FI \"PID eq " + pid + "\" | findstr " + pid + "\"";
    }
    else {
        //ps exit code 0 if process exists, 1 if it doesn't
        line = "ps -p " + pid;
            //`-p` is POSIX/BSD-compliant, `--pid` isn't<ref>https://github.com/apache/storm/pull/296#discussion_r20535744</ref>
    }
    CommandLine cmdLine = CommandLine.parse(line);
    DefaultExecutor executor = new DefaultExecutor();
    // disable logging of stdout/strderr
    executor.setStreamHandler(new PumpStreamHandler(null, null, null));
    // disable exception for valid exit values
    executor.setExitValues(new int[]{0, 1});
    // set timer for zombie process
    ExecuteWatchdog timeoutWatchdog = new ExecuteWatchdog(timeunit.toMillis(timeout));
    executor.setWatchdog(timeoutWatchdog);
    int exitValue = executor.execute(cmdLine);
    // 0 is the default exit code which means the process exists
    return exitValue == 0;
}

1
感谢此处推荐的PID方法检查。最终我被迫在Python中使用这种方法。 https://dev59.com/CVgQ5IYBdhLWcg3wIAWb敬礼 - 99Sono

3
在posix系统中,查询pid是否运行的典型方法是发送一个空信号,例如kill(pid, 0)。如果调用成功,则进程存在;如果返回ESRCH则不存在。这自然会产生不可避免的竞态条件,在现实中比理论上少得多。更不规则的方法是读取/proc文件系统(如果操作系统有的话),这需要更多的工作,实际上也是一样的,并且仍然受到相同的竞争条件。
请注意,pid锁文件技术可以是分为两个层次。也就是说,正在运行的进程创建文件,锁定它并写入其pid。它在运行期间保持锁定状态,因此这基本上消除了上述需要查询进程是否正在运行的需求,因为如果进程崩溃,文件锁将自动释放,即使文件仍然存在。逻辑如下:
if file exists
   if can get lock
       prev instance died unnaturally
       continue with this new process
   else
       instance already running
else
   good to go, continue with new process

这项技术也存在竞争条件。
我记不清Java是否有像kill或文件锁系统调用(如flocklockffcntl)的包装器来实现此方案。

我们有另一个用Python编写的应用程序可以完全做到这一点。我只需要找出一种以最简洁的方式获取特定PID存在性的方法。 - ElCapitaine
kill 是你最好、最简单的选择,尽管它有一些小缺陷。再说一遍,如果你锁定了文件,你其实不需要这样做。 - Duck

3
请看下面可直接复制/粘贴的示例。
布尔方法 isStillAllive(...) 将以进程 ID 号(pid)作为参数。方法调用非常通用,旨在包装与设备相关的逻辑,以解决类似于 Windows/Linux/Unix 等操作系统上的相同问题。
public boolean isStillAllive(String pidStr) {
    String OS = System.getProperty("os.name").toLowerCase();
    String command = null;
    if (OS.indexOf("win") >= 0) {
        log.debug("Check alive Windows mode. Pid: [{}]", pidStr);
        command = "cmd /c tasklist /FI \"PID eq " + pidStr + "\"";            
    } else if (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0) {
        log.debug("Check alive Linux/Unix mode. Pid: [{}]", pidStr);
        command = "ps -p " + pidStr;            
    } else {
        log.warn("Unsuported OS: Check alive for Pid: [{}] return false", pidStr);
        return false;
    }
    return isProcessIdRunning(pidStr, command); // call generic implementation
}

实际的系统调用被委托给isProcessIdRunning()。这个方法将以通用的方式调用已预设的系统相关命令,并逐行解析系统获得的结果。如果至少有一行回应包含pid,则我们认为这是成功的。

private boolean isProcessIdRunning(String pid, String command) {
    log.debug("Command [{}]",command );
    try {
        Runtime rt = Runtime.getRuntime();
        Process pr = rt.exec(command);

        InputStreamReader isReader = new InputStreamReader(pr.getInputStream());
        BufferedReader bReader = new BufferedReader(isReader);
        String strLine = null;
        while ((strLine= bReader.readLine()) != null) {
            if (strLine.contains(" " + pid + " ")) {
                return true;
            }
        }

        return false;
    } catch (Exception ex) {
        log.warn("Got exception using system command [{}].", command, ex);
        return true;
    }
}

1
可能最好解释一下这段代码是为了解决什么问题。 - Al Lelopath

1
这是与平台无关的。
ProcessHandle.of(pid).orElseThrow(() -> new 
      IllegalArgumentException("Given pid is not found"))
      .onExit();

如果pid未运行,它将抛出IllegalArgumentException异常,因此您可以安全地启动此新的Java应用程序实例。

它将返回一个Future<ProcessHandle>,这样您就可以使用future.get()等待它完成或仅检查使用future.isDone()记录某些内容或只是执行System.exit(1)


0

我知道这个问题已经提了很久,但是我在使用Java检查特定进程ID是否存活时遇到了一些困难,所以这里是我的解决方案:

private boolean isServiceRunnign(String servicePid) throws IOException {

    try {
        System.out.println("-- checking for services --");
        System.out.println("--" + System.currentTimeMillis() + "--");

        Process p = Runtime.getRuntime().exec("ps -p " + servicePid);
        System.out.println("Command : "+ "ps -p " + servicePid);

        BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
        String s = "";
        while ((s = br.readLine()) != null) {
            System.out.println("Bash outputs : " + s);
        }
        br.close();
        p.waitFor();
        // printing out exitValue for debug purposes.
        System.out.println("Exit: " + p.exitValue());
        if (p.exitValue() == 0) {
            System.out.println("-- " + servicePid + " found --");
            return true;
        }

        p.destroy();

        System.out.println("-- " + servicePid + " not found --");
        return false;
    } catch (Exception e) {
        System.out.println("-- " + servicePid + " not found --");
        System.out.println("Exception : " + e.toString());
        return false;
    }
}

*请注意,这仅适用于Linux操作系统。

基本上它运行以下命令:

ps -p <pid>

命令并检查退出值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接