Почему процесс зависает, если родитель не использует stdout/stderr в Java?

Я знаю, что если вы используете ProcessBuilder.start в Java для запуска внешнего процесса вы должны использовать его stdout/stderr (например, см. здесь). В противном случае внешний процесс зависает при запуске.

Мой вопрос, почему это работает так. Я предполагаю, что JVM перенаправляет stdout / stderr выполненного процесса на каналы, и если каналы не имеют места, записи в блок каналов. Имеет ли это смысл?

Теперь мне интересно, почему Java так поступает. Каково обоснование этого дизайна?

2 ответа

Решение

Ява ничего не делает в этой области. Он просто использует службы ОС для создания каналов.

В этом отношении все Unix-подобные ОС и Windows ведут себя одинаково: между родителем и потомком создается канал с 4K. Когда этот канал заполнен (потому что одна сторона не читает), процесс записи блокируется.

Это стандарт с момента появления труб. Ява мало что может сделать.

Можно утверждать, что API-интерфейс процесса в Java неуклюж и не имеет хороших значений по умолчанию, таких как простое подключение дочерних потоков к тому же stdin/stdout, что и родительский, если разработчик не переопределит их чем-то конкретным.

Я думаю, что есть две причины для текущего API. Прежде всего, разработчики Java (то есть парни из Sun/Oracle) точно знают, как работает API процесса и что вам нужно делать. Они знают так много, что им не пришло в голову, что API может сбить с толку.

Вторая причина в том, что нет хорошего дефолта, который будет работать для большинства. Вы не можете действительно соединить stdin родителя и ребенка; если вы наберете что-то на консоли, к какому процессу должен идти ввод?

Точно так же, если вы подключите стандартный вывод, вывод куда-нибудь пойдет. Если у вас есть веб-приложение, возможно, нет консоли, или вывод может идти куда-то, где никто не будет этого ожидать.

Вы не можете даже выдать исключение, когда канал заполнен, так как это может произойти и во время нормальной работы.

Это объясняется в Javadoc процесса:

По умолчанию созданный подпроцесс не имеет собственного терминала или консоли. Все его стандартные операции ввода-вывода (т.е. операции stdin, stdout, stderr) будут перенаправлены в родительский процесс, где к ним можно получить доступ через потоки, полученные с помощью методов getOutputStream(), getInputStream() и getErrorStream(). Родительский процесс использует эти потоки для подачи входных данных и получения выходных данных из подпроцесса. Поскольку некоторые собственные платформы предоставляют ограниченный размер буфера только для стандартных входных и выходных потоков, невозможность оперативной записи входного потока или чтения выходного потока подпроцесса может привести к блокировке или даже взаимной блокировке подпроцесса.

Другие вопросы по тегам