如何检测子进程是否因为其输出缓冲区已满而挂起?

时间:2016-05-19 23:45:31

标签: java process buffer

考虑以下Bash脚本和Java程序:

$ cat kb.sh
#!/bin/bash

# Prints $1 KB - fold adds a \n
tr '\0' '=' < /dev/zero | fold -w 1023 | head -n ${1:-10}

$ cat Demo.java 
import java.util.concurrent.TimeUnit;

class Demo {
  public static void main(String[] args) throws Exception {
    Process p = Runtime.getRuntime()
        .exec("/tmp/kb.sh " + (args.length > 0 ? args[0] : ""));
    if (p.waitFor(10, TimeUnit.SECONDS)) {
      System.out.println("Process terminated");
    } else {
      System.err.println("Process did not terminate");
      p.destroy();
      System.exit(1);
    }
  }
}

Demo类作为子进程启动kb.sh,并期望它快速终止。 kb.sh就其本身而言,输出(可能很快),一些KB的数据。我们可以在实践中验证它是否能够快速运行:

$ time /tmp/kb.sh 10000 | wc
  10000   10000 10240000

real    0m0.398s
user    0m0.178s
sys 0m0.030s

当我们运行Demo类时,我们会看到不同的行为:

$ java -cp . Demo 64
Process terminated

$ java -cp . Demo 65
Process did not terminate

如果我们尝试打印~65KB,它会挂起。我知道为什么 - Process正在缓冲子进程&#39;输出,当缓冲区满,子进程阻塞,直到通过Process.getInputStream()从缓冲区中读出一些数据。如果您在p.waitFor()之前添加了对ByteStreams.exhaust(p.getInputStream());的调用,则该过程将始终成功终止。

我的问题是,是否有任何方式在Java 中检测子进程何时被阻止?我担心答案可能是&#34;并非没有反思&#34;,因为我在任何相关的API中都没有看到任何这样的机制,但我可能会遗漏一些东西。

要预防不可避免的&#34;为什么要这样做?&#34;,我正在编写一个诊断实用程序,以便在现有的Process实例中检测到这一点,因为它一个持续的(和邪恶的)错误来源。我不想操纵Process或做任何破坏性的事情,我只是想检测过程因完全缓冲而停止的时间,以便我可以提醒来电者。

注意:依赖于操作系统的解决方案,例如检查ps的输出,是可以接受的,但显然不像只有Java的解决方案那样理想。

2 个答案:

答案 0 :(得分:0)

你不必检测它。您必须从标准输出和标准错误中消耗其所有输出。如果您的代码不能执行此操作,请修复它。

答案 1 :(得分:0)

简短回答:没有办法检测完整缓冲区是否是挂起子进程的原因。

更长的答案:Java I / O流API不提供任何确定方法 缓冲流的状态。您无法确定缓冲区是否已满。更糟糕的是,您甚至无法知道缓冲区中有多少可用空间,因此确定下一个write()操作是否会阻塞(如果不是不可能的话)也很困难。当然,一旦它被阻止,它就不会对任何事情作出反应。

没有要求儿童过程产生或响应心跳&#34;用来证明它活着而不是挂着 - 一旦它挂在那里就无法知道为什么 - 你无法主动或反应性地做到这一点处理除了读取之外的全缓冲流。

相关问题