Java, подпроцессы и непрочитанные потоки вывода: когда происходит тупик? - PullRequest
2 голосов
/ 21 февраля 2012

При создании подпроцессов в java с использованием Runtime.exec() я знаю, что мне нужно заполнить входной / выходной потоки, чтобы предотвратить блокировку подпроцесса.

Интересно, что в javadoc Process сказано немного больше:

...failure to promptly write the input stream or read the output stream of
the subprocess may cause the subprocess to block, and even deadlock.

Мне интересно, что в этой ситуации подпроцесс может также тупик !

Вопросы:
1. При каких условиях он заходит в тупик?
2. Почему он заходит в тупик?
3. Можете ли вы предоставить краткий пример программы, которая показывает этот тупик?
4. Является ли этот тупик ошибкой в ​​ОС?

Ответы [ 2 ]

2 голосов
/ 19 декабря 2012

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

Рассмотрим этот код:

final int LINES = 10;
// "tr" is a Unix command that translates characters;
// Here, for every line it reads, it will output a line with
// every 'a' character converted to 'A'.  But any command that outputs
// something for every line it reads (like 'cat') will work here
Process p = Runtime.getRuntime().exec("tr a A");
Writer out = new OutputStreamWriter(p.getOutputStream());
for (int i = 0; i < LINES; i++) {
    out.write("abcdefghijklmnopqrstuvwxyz\n");
}
out.close();
// Read all the output from the process and write it to stdout
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
    System.out.println(line);
}

Для небольших значений lines он будет работать нормально;все выходные данные tr могут помещаться в буфер ОС, прежде чем мы начнем его читать.Однако для больших значений (должно быть достаточно> 10000) буфер ОС будет заполнен;внутри команды tr вызовы write будут блокироваться, ожидая, когда будет опустошен буфер, и, в свою очередь, буфер, в который пишет код Java, заполнится (поскольку tr заблокирован, предотвращая егоот чтения со своего входа), в свою очередь блокируя наши вызовы out.write, что приводит к тупику, когда оба процесса ожидают записи в полные буферы, из которых не ведется активное чтение.

Эта тупиковая ситуация не является ошибкой в ​​ОС, так как ограниченный размер буфера для межпроцессного взаимодействия является преднамеренным проектным решением.Альтернатива (неограниченный размер буфера) имеет некоторые недостатки:

  • Может исчерпать память ядра
  • Чтобы избежать вышеперечисленного, если буферы автоматически «перетекают» на диск, это может привести к непредсказуемостипроизводительность и потенциально может заполнить диск.

Кроме этого, тупиковая ситуация может также возникать из-за внутренних буферов.Предположим, что, чтобы попытаться устранить вышеуказанную тупиковую ситуацию, мы изменили наш Java-код, написав одну строку, а затем поочередно прочитав одну строку.Однако обычно для процессов Linux не сбрасывается после каждой строки, когда они не записывают напрямую в терминал .Таким образом, tr может прочитать строку и записать ее в свой выходной буфер libc, а затем заблокировать, ожидая записи следующей строки - и наш Java-код заблокирует ожидание tr для вывода строки.

1 голос
/ 21 февраля 2012

В тупике всегда участвуют как минимум 2 участника. Таким образом, не только подпроцесс может заблокировать блокировку, но и комбинация родительского процесса и подпроцесса.

Для примера:

Подпроцесс хочет прочитать строку, обработать ее и распечатать результат. Родительский процесс считает, что он может сначала прочитать выходные данные подпроцесса, а затем только затем предоставить входные данные.

Результат: тупик, оба процесса ждут ввода от другого.

И, следовательно, нет, это не ошибка в ОС, а логическая ошибка в (родительском) процессе.

...