Java ProcessBuilder: Результирующий Процесс Зависает

Я пытался использовать ProcessBuilder Java для запуска приложения в Linux, которое должно работать "долгосрочно". Способ запуска этой программы-запустить команду (в этом случае я запускаю приложение воспроизведения мультимедиа), разрешить ей запуск и проверить, не разбился ли она. Например, проверьте, активен ли PID, а затем перезапустите процесс, если он умер.

проблема, которую я получаю прямо сейчас, заключается в том, что PID остается живым в системе, но GUI для приложение зависает. Я попытался переместить ProcessBuilder (cmd).start () в отдельный поток, но это, похоже, ничего не решает, как я надеялся.

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

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

Edit: я не беспокоюсь о потоке ввода-вывода, созданном из процесса, и, таким образом, не предпринял никаких шагов, чтобы справиться с этим-может ли это вызвать зависание в самом процессе?

8 ответов


Если процесс пишет в stderr или stdout, и вы его не читаете - он просто "зависнет" , блокируя при записи в stdout/err. Либо перенаправить stdout / err в /dev/null с помощью оболочки, либо объединить stdout / err с redirectErrorStream(true) и создать другой поток, который читает из stdout процесса


вы хотите на трюк?

Не начинайте процесс с ProcessBuilder.start (). Не пытайтесь возиться с перенаправлением/потреблением потока с Java (особенно если вы не даете об этом никакого s**t ; )

использовать ProcessBuilder.start () чтобы запустить небольшой скрипт оболочки, который поглощает все входные / выходные потоки.

что-то вроде этого:

#!/bin/bash

nohup  >/dev/null 2>error.log &

то есть: если вы не заботитесь о stdout и еще хочу журнал stderr (do you?) в файл (ошибка.log здесь).

Если вы даже не заботитесь о stderr, просто перенаправьте его на stdout:

#!/bin/bash

nohup  >/dev/null 2>1 &

и вы вызываете этот крошечный скрипт из Java, давая ему в качестве аргумента имя процесса, который вы хотите запустить.

Если процесс, запущенный в Linux, который перенаправляет stdout и stderr в /dev / null, все еще производит что-нибудь тогда у вас есть сломанная, несовместимая установка Linux ;)

другими словами: выше просто работает [TM] и избавляется от проблемного "вам нужно потреблять потоки в этом и том порядке бла-бла-бла Java-специфический не-смысл".


поток, выполняющий процесс, может блокировать, если он не обрабатывает выходные данные. Это можно сделать, создав новый поток, который считывает выходные данные процесса.

    final ProcessBuilder builder = new ProcessBuilder("script")
                    .redirectErrorStream(true)
                    .directory(workDirectory);

    final Process process = builder.start();
    final StringWriter writer = new StringWriter();

    new Thread(new Runnable() {
        public void run() {
            IOUtils.copy(process.getInputStream(), writer);
        }
    }).start();

    final int exitValue = process.waitFor();
    final String processOutput = writer.toString();

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

ProcessBuilder myProc2 = new ProcessBuilder(command);
final Process process = myProc2.start();

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

ProcessBuilder myProc2 = new ProcessBuilder(command);
myProc2.redirectErrorStream(true);        
final Process process = myProc2.start();
InputStream myIS = process.getInputStream();
String tempOut = convertStreamToStr(myIS);

и он снова начал работать. (см. эту ссылку для кода convertStreamToStr (): http://singztechmusings.wordpress.com/2011/06/21/getting-started-with-javas-processbuilder-a-sample-utility-class-to-interact-with-linux-from-java-program/)


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

Если вы не читаете выходные потоки, созданные процессом, то возможно, что приложение заблокирует, как только буферы приложения будут заполнены. Я никогда не видел, чтобы это происходило в Linux (хотя я не говорю, что это не так), но я видел эту точную проблему в Windows. Я думаю, это, вероятно, связано.


JDK7 будет иметь встроенную поддержку для перенаправления ввода-вывода подпроцесса:

http://download.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html

тем временем, если вы действительно хотите отказаться от stdout / stderr, лучше всего (в Linux) вызвать ProcessBuilder по команде, которая выглядит так:

["/bin/bash", "-c", "exec YOUR_COMMAND_HERE >/dev/null 2>&1"]

в случае, если вам нужно захватить stdout и stderr и контролировать процесс, то с помощью Apache Commons Exec мне очень помогли.


Я считаю, что проблема заключается в буферизации канала от самого Linux.

попробуйте использовать stdbuf с исполняемым файлом

new ProcessBuilder().command("/usr/bin/stdbuf","-o0","*executable*","*arguments*");**

на -o0 говорит не буферизировать вывод. То же самое относится к -i0 и -e0 если вы хотите unbuffer входной сигнал и труба ошибки.