12

我知道如果你ProcessBuilder.start在 Java 中使用来启动一个外部进程,你必须使用它的 stdout/stderr(例如,见这里)。否则,外部进程会在启动时挂起。

我的问题是为什么它会这样工作。我的猜测是 JVM 将已执行进程的 stdout/stderr 重定向到管道,如果管道没有空间,则写入管道会阻塞。是否有意义?

现在我想知道为什么Java 会这样做。这种设计背后的基本原理是什么?

4

2 回答 2

12

Java 在这方面没有做任何事情。它只是使用操作系统服务来创建管道。

在这方面,所有类似 Unix 的操作系统和 Windows 的行为都是相同的:在父子节点之间创建一个 4K 的管道。当该管道已满时(因为一侧没有读取),写入过程会阻塞。

这是自管道诞生以来的标准。Java 无能为力。

您可以争辩的是,Java 中的流程 API 很笨拙,并且没有像简单地将子流连接到与父流相同的标准输入/标准输出这样的良好默认值,除非开发人员用特定的东西覆盖它们。

我认为目前的 API 有两个原因。首先,Java 开发人员(即 Sun/Oracle 的人)确切地知道流程 API 是如何工作的以及您需要做什么。他们知道这么多,以至于他们没有想到 API 可能会令人困惑。

第二个原因是没有适合大多数人的良好默认设置。你不能真正连接父母和孩子的标准输入;如果你在控制台上输入一些东西,输入应该去哪个进程?

同样,如果您连接标准输出,输出将到达某个地方。如果您有一个 Web 应用程序,则可能没有控制台,或者输出可能会出现在没有人预料到的地方。

当管道已满时,您甚至不能抛出异常,因为这也可能在正常操作期间发生。

于 2013-06-07T11:58:48.113 回答
8

在Process的 javadoc 中有解释:

默认情况下,创建的子进程没有自己的终端或控制台。它的所有标准I/O(即stdin、stdout、stderr)操作都将被重定向到父进程,在那里可以通过使用getOutputStream()、getInputStream() 和getErrorStream() 方法获得的流来访问它们。父进程使用这些流向子进程提供输入并从子进程获取输出。由于部分原生平台只为标准输入输出流提供有限的缓冲区大小,未能及时写入子进程的输入流或读取输出流可能会导致子进程阻塞,甚至死锁。

于 2013-06-07T11:56:29.287 回答