Возможности Process.exitValue() и Process.destroy()

Я экспериментировал с Process и ProcessBuilder и пришел с этим SSCCE.

        import java.io.IOException;
        public class TestProcess {
            public static void main(String[] args) {
                Process process = null;
                ProcessBuilder pb = new ProcessBuilder("notepad.exe");

                try {
                    process = pb.start();
                } catch (IOException e) {e.printStackTrace();}

                //have some time to close notepad
                try {
                    Thread.sleep(10*1000);
                } catch (InterruptedException ignored) {}

                try {
                    System.out.println(process.exitValue());
                 } catch (IllegalThreadStateException e) {
                    System.out.println(e);
                 }

                 if (process != null)
                     process.destroy();

                 /*try {
                     Thread.sleep(0, 1);
                 } catch (InterruptedException ignored) {}*/

                 System.out.println(process.exitValue());
             }
         }
  1. Если я запущу этот код и закрою блокнот до истечения 10-секундного тайм-аута. destroy() вызов не показывает никаких проблем при попытке остановить уже завершенный процесс. Почему?
  2. Если запустить этот код и вообще не закрывать блокнот (с прокомментированным вторым сном)

Кажется, что уничтожение - это асинхронный вызов (просто посылка сигнала?), который приводит к исключению во второй exitValue()

 java.lang.IllegalThreadStateException: process has not exited
 Exception in thread "main" java.lang.IllegalThreadStateException: process has not exited
        at java.lang.ProcessImpl.exitValue(ProcessImpl.java:246)
        at TestProcess.main(TestProcess.java:30)
  1. Если я запускаю этот код и вообще не закрываю блокнот (с незакомментированным вторым сном), то второй exitValue никогда не выдает исключение, даже если значение сна составляет всего 1 мс. Это из-за sleep() накладных расходов? Второй exitValue вернет 1.

PS. Я запускаю его из Windows 7 и Eclipse.


person Nikolay Kuznetsov    schedule 20.12.2012    source источник
comment
1) } catch (IOException ignored) {} Извлеките свои ошибки из песка и вместо этого } catch (IOException e) { e.printStackTrace(); } 2) Убедитесь, что код реализует рекомендации, когда Runtime.exec() не будет 3) Чтобы быть точным, SSCCE требует импорта.   -  person Andrew Thompson    schedule 20.12.2012
comment
@AndrewThompson, 1) я уверен, что он всегда может запустить блокнот в Windows, 2) что вы имеете в виду? 3) да, только один импорт.   -  person Nikolay Kuznetsov    schedule 20.12.2012
comment
@AndrewThompson, спасибо, я обновил код для 1) и 3).   -  person Nikolay Kuznetsov    schedule 20.12.2012


Ответы (3)


  1. Почему показывает проблему? Вы пытаетесь уничтожить процесс, который уже был уничтожен. Спецификация Process.destroy() не говорит, что произойдет, если нечего будет разрушать, поэтому логично (я полагаю) предположить, что если разрушать нечего, то и жаловаться не на что. Сравните с Thread.join(), который не просто умирает, если поток уже закончился.

  2. Единственный способ убить процесс — послать ему сигнал. В некоторых ОС есть другие, более "жестокие" способы (на некоторых платформах, например, можно просто удалить процесс из списка запущенных процессов ОС. Результаты не определены и обычно это заканчивается некрасиво), но по крайней мере с платформами, о которых я знаю, все дело в отправке сигналов.

  3. Возможно, действительно, это потому, что для вызова Thread.sleep() требуется время. Попробуйте увеличить значение тайм-аута.

person Isaac    schedule 20.12.2012
comment
Спасибо за ответ. 3. Я постепенно уменьшал значение с миллисекунд до 1 наносекунды. Во всех случаях, если sleep() вызывается второй, exitValue() возвращает 1 и не генерирует исключение. - person Nikolay Kuznetsov; 20.12.2012
comment
Я понимаю. Что касается (3), то у меня нет объяснений. Я посмотрел код ProcessImpl в Windows; exitValue() вызывает нативную функцию, которая, как я полагаю, в конечном итоге вызывает Windows GetExitCodeProcess. В документации ни к одному из методов ничего не упоминается о произвольном коде выхода 1 для процесса, который на самом деле не завершился. Действительно странно. - person Isaac; 20.12.2012
comment
Я считаю, что он заканчивается, если возвращается 1, иначе он выдает исключение. Итак, я предполагаю, что sleep занимает гораздо больше времени, достаточного для завершения процесса. - person Nikolay Kuznetsov; 20.12.2012
comment
Чтобы сделать это, вам нужно проверить, является ли 1 задокументированным формальным кодом выхода для Notepad. - person Isaac; 20.12.2012
comment
Если я закрою его вручную перед первым сном, оба exitValue напечатают 0 - person Nikolay Kuznetsov; 20.12.2012
comment
да. Когда вы изящно закроете notepad.exe, его код выхода должен быть (и есть) 0. Вопрос в том, есть ли какая-либо документация относительно обстоятельств, при которых notepad.exe вернет код выхода 1? и если да, то каковы эти обстоятельства? - person Isaac; 20.12.2012

Я ожидаю, что метод destroy() вызывает родную функцию Windows TerminateProcess. Глядя на MSDN , Я нашел это:

TerminateProcess является асинхронным; он инициирует завершение и немедленно возвращается. Если вам нужно убедиться, что процесс завершился, вызовите функцию WaitForSingleObject с дескриптором процесса.

Поэтому я думаю, что это объясняет, что уничтожение действительно асинхронно.

Еще одна выдержка из того же источника:

Функция TerminateProcess используется для безусловного завершения процесса.

Я предполагаю, что «безоговорочно» может объяснить, почему вызов destroy() в завершающем процессе не терпит неудачу.

Надеюсь, это поможет. (очень интересный вопрос!)

person ben75    schedule 20.12.2012

ProcessImpl.java в методе destroy вызовите собственную функцию terminateProcess:

public void destroy() { terminateProcess(handle); }

private static native void terminateProcess(long handle);

terminateProcess зависит от платформы, а для Windows вы можете найти исходники здесь. Это просто вызов функции Windows TerminateProcess (ссылка на эту функцию была в предыдущем ответе или вы можете погуглить) с uExitCode=1 - поэтому код выхода уничтоженного процесса - 1.

В Linux похоже используется что-то похожее на это. И в качестве доказательства следующий код возвращает 143 в Ubuntu, что соответствует SIGTERM (https://stackoverflow.com/a/4192488/3181901):

public static void main(final String[] args) throws IOException, InterruptedException {
    final Process process = Runtime.getRuntime().exec(args[0]);
    process.destroy();
    Thread.sleep(1000);
    System.out.println(process.exitValue());
}
person knok16    schedule 18.02.2015