是否可以为不是从我的代码启动的进程创建 java.lang.Process 实例?

San*_*ors 3 java process selenium-chromedriver

我有一个进程(在 Windows 上名为chromedriver.exe),它是在我创建Selenium Chrome Driver的新实例时创建的。

所以我不会自己开始这个过程,但java.lang.Process如果可能的话,我想要一个代表该过程的实例。

我想创建这样的实例的原因是我想Process.waitFor()在我发出(如果在 Windows 上)a 之后调用等待直到进程实际终止Runtime.getRuntime().exec("taskkill /F /IM chromedriver.exe")

我不能仅针对此特定需求引入对第三方库的新依赖。我可以使用 Apache Commons 中的任何东西。

所以问题是:有没有办法从我的代码中获取一个Process代表未从我的代码启动的正在运行的进程的实例?

我希望能够终止该进程的原因是像 JVM 崩溃这样的突然终止可能不会WebDriver.quit()终止该进程。因此,在创建导致进程启动的 ChromeDriver 实例之前,我想终止任何可能已经存在的此类进程。

我使用exec("taskkill...")在 Windowskillall上终止进程的原因(我在 Linux 上使用)是到目前为止我发现的唯一方法,但是如果有可能获得Process代表该进程的实例,我也会尝试使用Process.destroy(),以查看是否在提到的“子”其描述“杀死子进程”指的是这一进程。

Hug*_* M. 5

简而言之,不是用 Java 吗?8,但可以使用 Java 9java.lang.ProcessHandle.

ProcessJava 中的A并不是要对任意操作系统进程进行建模,它实际上是对 java 进程子进程进行建模,因此您不能附加到另一个正在运行的进程,并期望获得 a 的所有功能Process

Process 类提供了执行进程输入、执行输出到进程、等待进程完成、检查进程退出状态以及销毁(杀死)进程的方法。

如您所见,输入和输出要求您的 java 进程拥有另一个进程的 stdin/stdout/stderr 的句柄,这仅对子进程有意义。该文档还到处提到“子进程”,因此暗示它是一个子进程。

在 Java 9 中,文档明确以:

Process 提供对由ProcessBuilder.start和启动的本机进程的控制Runtime.exec

但是 Java 9 也带来了ProcessHandle,这正是您所需要的:

ProcessHandle识别并提供对本机进程的控制。可以监视每个单独的进程的活跃度、列出其子进程、获取有关进程的信息或销毁它。相比之下,Process实例由当前进程启动,并额外提供对进程输入、输出和错误流的访问。

因此,如果您可以使用 Java 9 运行您的测试,您可以尝试使用ProcessHandle.allProcesses()找到您想要的进程,然后使用 杀死它.destroy(),使用 监视其活跃度isAlive(),或者使用onExit().

Optional<ProcessHandle> optional = ProcessHandle.allProcesses().filter(process -> {
    Optional<String> command = process.info().command();
    return command.isPresent() && command.get().equals("Chromedriver.exe");
}).findFirst();
if (optional.isPresent()) {
    ProcessHandle processHandle = optional.get();
    System.out.println("Killing process " + processHandle.pid());
    processHandle.destroy();
    try {
        System.out.println("Waiting for process " + processHandle.pid() + " to exit...");
        processHandle.onExit().get();
        System.out.println("Done !");
    } catch (InterruptedException|ExecutionException e) {
        e.printStackTrace();
    }
}
Run Code Online (Sandbox Code Playgroud)

话虽如此,如果您真的想要,您可以Process自己创建一个子类,它将提供有限的功能(即与 I/O 无关,只有waitFor等等destroy)。

下面是一个可怕的例子,它使用这个 Q&A中的想法为 Windows 实现快速破解。

  • 使用名称查找进程 tasklist /FO CSV /FI "IMAGENAME eq Chromedriver.exe", to get the PID
  • 杀死它 taskkill
  • 等待它以使用的轮询循环结束 tasklist /FO CSV /FI "PID eq ..."

Linux版本的使用pgrepkill/proc/{pid}/

请注意,这不是真正有用的包裹,作为一个实际的例子java.lang.Process,所以我分开了工具类的顾虑实现所需功能的Windows,和子类Process作为一个练习来实现显示什么是可能的,什么是不可能(代码中的详细信息)。因此,如果您需要,最好ProcessTool直接使用,而不是DummyProcess.

(我已经在另一个过程中对此进行了测试,我不能说Chromedriver.exe,您可能需要调整一些细节)

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.Scanner;

public class WaitForProcessDemo {

    public static void main(String[] args) throws IOException, InterruptedException {
        Process p = getDummyProcess("Chromedriver");
        System.out.println("Killing...");
        p.destroy();
        System.out.println("Waiting...");
        p.waitFor();
        System.out.println("Done.");
    }

    private static Process getDummyProcess(String exeName) throws IOException {
        ProcessTool tool = new WindowsProcessTool();
        long pid = tool.findProcessByName(exeName);
        return new DummyProcess(pid, tool);
    }

    interface ProcessTool {
        long findProcessByName(String exeName) throws IOException;
        void killProcess(long pid) throws IOException;
        boolean isPidStillThere(long pid);
    }

    private static class WindowsProcessTool implements ProcessTool {

        @Override
        public long findProcessByName(String exeName) throws IOException {
            String processInfoCSV = findProcessInfoCSV("IMAGENAME eq " + exeName);
            String[] fields = processInfoCSV.split("\"");
            String pid = fields[3];
            return Integer.parseInt(pid);
        }

        String findProcessInfoCSV(String filter) throws IOException {
            Process p = new ProcessBuilder("tasklist", "-FO", "CSV", "/FI", filter)
                    .redirectErrorStream(true)
                    .start();
            Scanner scanner = new Scanner(p.getInputStream());
            scanner.nextLine(); // skip header line
            if (scanner.hasNextLine()) {
                return scanner.nextLine();
            }
            throw new IOException("No such process: " + filter);
        }

        @Override
        public void killProcess(long pid) throws IOException {
            new ProcessBuilder("taskkill", "/PID", String.valueOf(pid))
                    .redirectErrorStream(true)
                    .start();
        }

        @Override
        public boolean isPidStillThere(long pid) {
            try {
                findProcessInfoCSV("PID eq " + pid);
                return true;
            } catch (IOException e) {
                return false;
            }
        }
    }

    private static class LinuxProcessTool implements ProcessTool {

        @Override
        public long findProcessByName(String exeName) throws IOException {
            Process pgrep = new ProcessBuilder("pgrep", exeName)
                    .redirectErrorStream(true)
                    .start();
            Scanner scanner = new Scanner(pgrep.getInputStream());
            return Long.parseLong(scanner.nextLine());
        }

        @Override
        public void killProcess(long pid) throws IOException {
            new ProcessBuilder("kill", String.valueOf(pid))
                    .redirectErrorStream(true)
                    .start();
        }

        @Override
        public boolean isPidStillThere(long pid) {
            return Paths.get("/proc", String.valueOf(pid)).toFile().isDirectory();
        }
    }

    /*
     * Broken & incomplete implementation of java.lang.Process, implemented as an exercise.
     * (Kids, don't do this at home)
     */
    static class DummyProcess extends Process {

        private final long pid;
        private final ProcessTool tool;

        DummyProcess(long pid, ProcessTool tool) {
            this.pid = pid;
            this.tool = tool;
        }

        @Override
        public OutputStream getOutputStream() {
            return null; // DANGER. This cannot be implemented for non-child process.
        }

        @Override
        public InputStream getInputStream() {
            return null; // DANGER. This cannot be implemented for non-child process.
        }

        @Override
        public InputStream getErrorStream() {
            return null; // DANGER. This cannot be implemented for non-child process.
        }

        @Override
        public int waitFor() throws InterruptedException {
            // Very sub-optimal implementation
            boolean isPidPresent = isPidStillThere();
            while (isPidPresent) {
                Thread.sleep(500);
                isPidPresent = isPidStillThere();
            }
            return 0;
        }

        @Override
        public int exitValue() {
            // For example, this is dangerous, as Process.isAlive() will call this, and determine process is not alive.
            // Also, it cannot be implemented correctly, it's not possible to tell what was exit value.
            // At best we could throw IllegalThreadStateException when process is still alive.
            return 0;
        }

        @Override
        public void destroy() {
            try {
                tool.killProcess(pid);
            } catch (IOException e) {
                throw new RuntimeException("Failed to kill process " + pid, e);
            }
        }

        private boolean isPidStillThere() {
            return tool.isPidStillThere(pid);
        }
    }

}
Run Code Online (Sandbox Code Playgroud)