【问题标题】:Java ProcessBuilder: Resultant Process HangsJava ProcessBuilder:结果进程挂起
【发布时间】:2011-03-18 03:46:29
【问题描述】:

我一直在尝试使用 Java 的 ProcessBuilder 在 Linux 中启动一个应该“长期”运行的应用程序。该程序运行的方式是启动一个命令(在本例中,我正在启动一个媒体播放应用程序),允许它运行,并检查以确保它没有崩溃。例如,检查 PID 是否仍然处于活动状态,然后重新启动进程,如果它已经死亡。

我现在遇到的问题是 PID 在系统中仍然存在,但应用程序的 GUI 挂起。我尝试将 ProcessBuilder(cmd).start() 转移到一个单独的线程中,但这似乎并没有像我希望的那样解决任何问题。

基本上结果是,对于用户来说,程序似乎已经崩溃,但杀死驱动 ProcessBuilder.start() 进程的 Java 进程实际上允许创建的进程恢复其正常行为。这意味着 Java 应用程序中的某些东西正在干扰生成的进程,但我现在完全不知道是什么。 (因此我尝试将其分离到另一个线程中,但似乎没有解决任何问题)

如果有人有任何意见/想法,请告诉我,因为我一生都想不出如何解决这个问题。

编辑:我不关心从进程创建的 I/O 流,因此没有采取任何措施来处理这个问题——这会导致进程本身挂起吗?

【问题讨论】:

  • 既然你已经声明你没有处理流程的流,我必须插话说“是的,很可能这是问题的原因。阅读 stdout 和 stderr 的内容很重要,如果孩子希望你写到 stdin 也很重要”。检查这个 SO 问题是值得的:stackoverflow.com/questions/882772/…

标签: java linux crash multithreading processbuilder


【解决方案1】:

如果进程写入stderrstdout,而您没有阅读它 - 它只会“挂起”,在写入stdout/err 时阻塞。使用 shell 将 stdout/err 重定向到 /dev/null 或将 stdout/err 与 redirectErrorStream(true) 合并并生成另一个从进程的 stdout 读取的线程

【讨论】:

  • 看起来极其重要!您必须使用 ffmpeg 的标准输出/标准错误。至少我注意到发布/消费 rtmp 连接是这样的。
  • 那么,如果所有者进程终止并让子进程继续运行,会发生什么?
【解决方案2】:

你想要把戏吗?

不要从 ProcessBuilder.start() 开始您的流程。不要试图弄乱来自 Java 的流重定向/消费(尤其是如果您对此不屑一顾;)

使用 ProcessBuilder.start() 来启动一个小 shell 脚本来吞噬所有的输入/输出流。

类似的东西:

#!/bin/bash

nohup $1 >/dev/null 2>error.log &

也就是说:如果您不关心 stdout 并且仍想将 stderr(是吗?)记录到文件中(error.log 此处)。

如果您甚至不关心 stderr,只需将其重定向到 stdout:

#!/bin/bash

nohup $1 >/dev/null 2>1 &

然后你从 Java 中调用那个小脚本,将你想要运行的进程的名称作为参数提供给它。

如果在 Linux 上运行的将 stdout 和 stderr 都重定向到 /dev/null 的进程仍然产生任何内容,那么您的 Linux 安装已损坏、不合规;)

换句话说:上面的 Just Works [TM] 并摆脱有问题的“你需要以这个和那个顺序消费流 bla bla bla Java-specific non-sense”

【讨论】:

  • 让我们大笑起来 - 现在是星期五晚上,这让我们损失了我们的晚上 :)
【解决方案3】:

如果不处理输出,运行进程的线程可能会阻塞。这可以通过生成一个读取进程输出的新线程来完成。

    final ProcessBuilder builder = new ProcessBuilder("script")
                    .redirectErrorStream(true)
                    .directory(workDirectory);

    final Process process = builder.start();
    final StringWriter writer = new StringWriter();

    new Thread(new Runnable() {
        public void run() {
            IOUtils.copy(process.getInputStream(), writer);
        }
    }).start();

    final int exitValue = process.waitFor();
    final String processOutput = writer.toString();

【讨论】:

    【解决方案4】:

    在遇到类似问题后偶然发现了这一点。同意 nos,您需要处理输出。我有这样的事情:

    ProcessBuilder myProc2 = new ProcessBuilder(command);
    final Process process = myProc2.start();
    

    而且效果很好。生成的进程甚至确实输出了 some 输出,但并不多。当我开始输出更多时,似乎我的进程甚至不再启动了。我更新到这个:

    ProcessBuilder myProc2 = new ProcessBuilder(command);
    myProc2.redirectErrorStream(true);        
    final Process process = myProc2.start();
    InputStream myIS = process.getInputStream();
    String tempOut = convertStreamToStr(myIS);
    

    它又开始工作了。 (convertStreamToStr() 代码参考这个link

    【讨论】:

      【解决方案5】:

      编辑:我不关心从进程创建的 I/O 流,因此没有采取任何措施来处理这个问题——这会导致进程本身挂起吗?

      如果您不读取进程创建的输出流,那么一旦应用程序的缓冲区已满,应用程序可能会阻塞。我从未在 Linux 上看到过这种情况(尽管我并不是说它没有),但我在 Windows 上看到过这个确切的问题。我认为这可能是相关的。

      【讨论】:

        【解决方案6】:

        JDK7 将内置对子进程 I/O 重定向的支持:

        http://download.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html

        与此同时,如果您真的想丢弃 stdout/stderr,最好(在 Linux 上)在如下命令上调用 ProcessBuilder:

        ["/bin/bash", "-c", "exec YOUR_COMMAND_HERE >/dev/null 2>&1"]
        

        【讨论】:

          【解决方案7】:

          另一种解决方案是使用Redirect.PIPE 启动进程并像这样关闭InputStream

          ProcessBuilder builder = new ProcessBuilder(cmd);
          builder.redirectOutput(Redirect.PIPE);
          builder.redirectErrorStream(true); // redirect the SysErr to SysOut
          Process proc = builder.start();
          proc.getInputStream().close(); // this will close the pipe and the output will "flow"
          proc.waitFor(); //wait
          

          我在 Windows 和 Linux 中对此进行了测试,并且工作正常!

          【讨论】:

            【解决方案8】:

            如果您需要捕获 stdout 和 stderr 并监控进程,那么使用 Apache Commons Exec 对我有很大帮助。

            【讨论】:

              【解决方案9】:

              我认为问题出在 Linux 本身的缓冲管道上。

              尝试在您的可执行文件中使用stdbuf

              new ProcessBuilder().command("/usr/bin/stdbuf","-o0","*executable*","*arguments*");**
              

              -o0 表示不缓冲输出。 如果您想取消缓冲输入和错误管道,-i0-e0 也是如此。

              【讨论】:

                【解决方案10】:

                您需要在等待完成循环之前读取输出。如果输出未填满缓冲区,您将不会收到通知。如果是,它将等到您读取输出。

                假设您有一些关于您未阅读的命令的错误或响应。这将导致应用程序停止并 waitFor 永远等待。一种简单的解决方法是将错误重定向到常规输出。

                我在这个问题上花了 2 天时间。

                public static void exeCuteCommand(String command) {
                    try {
                        boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
                        ProcessBuilder builder = new ProcessBuilder();
                        if (isWindows) {
                            builder.command("cmd.exe", "/c", command);
                        } else {
                            builder.command("sh", "-c", command);
                        }
                        Process process = builder.start();
                        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
                        String line;
                        while ((line = reader.readLine()) != null)
                            System.out.println("Cmd Response: " + line);
                        process.waitFor();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  • 2012-07-30
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-05-17
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多