Java зависает, хотя выполнение скрипта завершено

Я пытаюсь выполнить скрипт из моего кода Java, который выглядит следующим образом:

Process p = Runtime.getRuntime().exec(cmdarray, envp, dir); // cmdarray is a String array
// consisting details of the script and its arguments

final Thread err = new Thread(...); // Start reading error stream
err.start();
final Thread out = new Thread(...); // Start reading output stream
out.start();
p.waitFor();
// Close resources 

Выполнение скрипта завершено (его больше нет), но Java застрял на методе waitFor () процесса !. И да, я читаю потоки вывода и ошибок в 2 отдельных потоках. Да, они объединяются в конце (после waitFor () ).

Сценарий в основном устанавливает несколько RPM (примерно 10 или около того) и настраивает их. Таким образом, сценарий выполняется чуть более 60 секунд.

Он выглядит примерно так:

#!/bin/sh

#exec 3>&1 >/var/log/some_log 2>&1

# If the above line is uncommented, Java recognizes that the 
# process is over and terminates fine.

tar xzf a-package-having-rpms.tar.gz
cd unpacked-folder
(sh installer-script.sh) #This installs 10 odd rpms in a subshell and configures them
cd ..
rm -rf unpacked-folder

exit 0

Довольно шокирующе, если я добавлю следующую строку в сценарий (вверху), Java поймет, что сценарий завершен, и он завершает процесс идеально.

exec 3>&1 > /var/log/some_log 2>&1

Для записи, сценарий не генерирует никакого вывода. Ноль символов! Так что помещать в них оператор exec не имеет смысла!

Но все же, как ни странно, добавление оператора exec в скрипт делает работу Java !. Почему ??

Как мне избежать этого нелогичного оператора exec в скрипте?

Если вас интересует, как выглядит installer-script.sh, то:

#!/bin/sh

exec 3>&1 >>/var/log/another-log.log 2>&1
INSDIR=$PWD
RPMSDIR=$INSDIR/RPMS
cd $RPMSDIR
#
rpm -i java-3.7.5-1.x86_64.rpm
rpm -i --force perl-3.7.5-1.x86_64.rpm
rpm -i --nodeps mysql-libs-5.0.51a-1.vs.i386.rpm
rpm -i --nodeps mysql-5.0.51a-1.vs.i386.rpm
rpm -i --nodeps mysql-server-5.0.51a-1.vs.i386.rpm
rpm -i --nodeps perl-DBD-MySQL-3.0007-2.el5.i386.rpm
rpm -i --nodeps perl-XML-Parser-2.34-6.1.2.2.1.i386.rpm
.
.
.

Теперь, почему Java-команда в первом сценарии требуется, чтобы Java знала, что процесс завершен? Как я могу избежать этого exec ?, esp. поскольку первый скрипт не выдает никаких результатов.

Ожидание ответов с замиранием сердца!

7
задан pavanlimo 14 August 2010 в 17:22
поделиться

7 ответов

Я предполагаю, что Java не считает, что сценарий завершен, пока каналы, переданные ему через stdin / stdout / stderr, не будут закрыты подпроцессом. То есть больше нет активных процессов чтения на stdin, нет более активных процессов записи на stdout / stderr.

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

Точно так же с каналом, который вы пишете, вы не получите сигнал «сломан канал», пока последний читатель не закроет канал.

Эта проблема обычно возникает, когда ваш скрипт отключает фоновые задачи (например, недавно установленные службы), которые наследуют stdin / stdout / stderr.

Используя exec, вы явно нарушаете цепочку наследования этих каналов, чтобы фоновые процессы не использовали их.

Если в Linux, проверьте / proc / * / fd на наличие каких-либо новых служб и посмотрите, совпадает ли их stdin / stdout / stderr с тем же конвейером, который ваш java-процесс передает вашему скрипту.

Такая же ситуация часто случается, когда вы запускаете скрипты /etc/init.d/xxx: они нормально завершаются, когда вы запускаете их из командной строки, но, кажется, зависают, когда вы запускаете их с какого-то монитора.

РЕДАКТИРОВАТЬ:

Вы говорите, что сценарий установщика содержит строку:

exec 3>&1 >>/var/log/another-log.log 2>&1

Первый термин, 3> & 1, клонирует стандартный вывод в файловый дескриптор 3 (см. Перенаправления в man bash). Насколько мне известно, fd 3 особого значения не имеет.Затем он заменяет stdout, открывая /var/log/another-log.log, и заменяет stderr, клонируя stdout. См. Раздел «Перенаправления» на странице руководства bash

. Это означает, что новый файловый дескриптор 3 открыт для записи в конвейер, который изначально был передан как STDOUT. Программы, которые должны быть демонами системных служб, часто закрывают файловые дескрипторы 0 (STDIN), 1 (STDOUT) и 2 (STDERR), но могут не беспокоиться о других. Кроме того, теперь, когда оболочка открыла FD-3, она передаст этот открытый файл любой выполняемой команде, включая фоновые команды.

Знаете ли вы, есть ли какая-то конкретная причина, по которой установщик открывает FD 3? Я предполагаю, что если вы просто удалите термин «3> & 1» из установщика, ваша проблема будет решена. Это позволит вам полностью удалить exec из вашего скрипта

7
ответ дан 7 December 2019 в 07:38
поделиться

Вероятная причина в том, что установка пакетов запускает фоновые процессы, которые оставляют один или оба ваших канала (stdout / stderr) открытыми. Поэтому ваши потоки не будут завершены. Это означает, что эти фоновые процессы не работают должным образом. Правильный демон заменяет исходный stdin / stdout / stderr на / dev / null или файл журнала после завершения инициализации.

1
ответ дан 7 December 2019 в 07:38
поделиться

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

0
ответ дан 7 December 2019 в 07:38
поделиться

Если вы работаете в Linux / Unix, попробуйте , а не ] потребляет stdout / stderr.

В прошлый раз, когда мне пришлось запускать внешний скрипт из Java, мне пришлось проверять ОС и присоединять потоки пожирателя вывода, только если в Windows.

0
ответ дан 7 December 2019 в 07:38
поделиться

Я не пробовал в Linux, но я столкнулся с этой проблемой в Windows. Кажется, мне пришлось закрыть стандартный поток процесса, прежде чем он завершился.

0
ответ дан 7 December 2019 в 07:38
поделиться

Похоже, что вы понимаете части и фрагменты проблемы, но все еще не совсем понимаете "большую картину".

  1. Да, можно вызвать сценарий оболочки из Java-программы. Легко, на самом деле! <= Вы уже знаете это.

  2. Да, в общем, вы должны быть в состоянии видеть все, что печатается в stderr или stdout.

  3. И да, вы должны быть в состоянии перенаправить stderr и/или stdout в файл.

  4. Ваша проблема, очевидно, в том, что вы не видите ТО, ЧТО вы ожидаете увидеть, КОГДА вы ожидаете это увидеть. Это нормально, это ожидаемо ... и, в более широкой схеме вещей, это ВЫСОКО ЖЕЛАТЕЛЬНО.

Основной проблемой, я считаю, является "буферизация ввода-вывода".

Буферизация - это хорошо.

Я настоятельно рекомендую вам: 1. Создайте простую, однопоточную тестовую программу Java с командной строкой, которая вызывает сценарий оболочки. <= Вы должны увидеть "ожидаемые результаты"

  1. Разработайте тестовую программу с потоками. В зависимости от структуры программы вы можете получить или не получить "ожидаемые результаты".

  2. Замените программу на языке C (или другую программу на Java) на сценарий оболочки. <= Это упрощает использование "stderr", который является UNBUFFERED, по сравнению с "stdout". Вам также следует отказаться от "exec" - это была красная селедка!

  3. Экспериментируйте и смотрите, что вы узнаете.

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

PS: И повторяйте мою мантру: "I/O buffering is Good" ;-)

PPS:

Q: Этот вопрос пока что просмотрели только 38 раз!

A: "Ты должен научиться терпению, юный кузнечик" ;-)

Q: Это вроде как критично для меня!

A: "Смирению ты тоже должен научиться" ;-)

0
ответ дан 7 December 2019 в 07:38
поделиться

Вы просто не можете запустить сценарий из Java и дождаться его. Вы должны выполнить интерпретатор этого сценария. Командный массив должен начинаться с «bash», а не «myscript.bash».

-1
ответ дан 7 December 2019 в 07:38
поделиться
Другие вопросы по тегам:

Похожие вопросы: