考虑以下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的解决方案那样理想。
答案 0 :(得分:0)
你不必检测它。您必须从标准输出和标准错误中消耗其所有输出。如果您的代码不能执行此操作,请修复它。
答案 1 :(得分:0)
简短回答:没有办法检测完整缓冲区是否是挂起子进程的原因。
更长的答案:Java I / O流API不提供任何确定方法 缓冲流的状态。您无法确定缓冲区是否已满。更糟糕的是,您甚至无法知道缓冲区中有多少可用空间,因此确定下一个write()操作是否会阻塞(如果不是不可能的话)也很困难。当然,一旦它被阻止,它就不会对任何事情作出反应。
没有要求儿童过程产生或响应心跳&#34;用来证明它活着而不是挂着 - 一旦它挂在那里就无法知道为什么 - 你无法主动或反应性地做到这一点处理除了读取之外的全缓冲流。