Подтвердить что ты не робот

Как запустить полностью независимый процесс из Java-программы?

Я работаю над программой, написанной на Java, которая для некоторых действий запускает внешние программы, используя настраиваемые пользователем командные строки. В настоящее время он использует Runtime.exec() и не сохраняет ссылку Process (запущенные программы являются либо текстовым редактором, либо архивной утилитой, поэтому нет необходимости в потоках системы /out/err ).

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

Я бы очень хотел, чтобы запущенные программы были полностью независимы от JVM, которые их запускали.

Целевая операционная система является множественной, поскольку Windows, Linux и Mac являются минимальными, но любая система GUI с JVM действительно является желаемой (отсюда и пользовательская конфигурация реальных команд).

Кто-нибудь знает, как запустить запущенную программу полностью независимо от JVM?


Изменить в ответ на комментарий

Код запуска выглядит следующим образом. Код может запускать редактор, расположенный в определенной строке и столбце, или может запускать средство просмотра архива. Цитируемые значения в сконфигурированной командной строке трактуются как кодированные ECMA-262 и декодируются, а кавычки разделяются, чтобы сформировать требуемый параметр exec.

Запуск происходит на EDT.

static Throwable launch(String cmd, File fil, int lin, int col) throws Throwable {
    String frs[][]={
        { "$FILE$"  ,fil.getAbsolutePath().replace('\\','/') },
        { "$LINE$"  ,(lin>0 ? Integer.toString(lin) : "") },
        { "$COLUMN$",(col>0 ? Integer.toString(col) : "") },
        };
    String[] arr; // array of parsed tokens (exec(cmd) does not handle quoted values)

    cmd=TextUtil.replace(cmd,frs,true,"$$","$");
    arr=(String[])ArrayUtil.removeNulls(TextUtil.stringComponents(cmd,' ',-1,true,true,true));
    for(int xa=0; xa<arr.length; xa++) {
        if(TextUtil.isQuoted(arr[xa],true)) {
            arr[xa]=TextDecode.ecma262(TextUtil.stripQuotes(arr[xa]));
            }
        }
    log.println("Launching: "+cmd);
    Runtime.getRuntime().exec(arr);
    return null;
    }

Это, похоже, происходит только тогда, когда программа запускается из моей среды IDE. Я закрываю этот вопрос, поскольку проблема существует только в моей среде разработки; это не проблема в производстве. Из тестовой программы в одном из ответов и последующем тестировании, которое я провел, я удовлетворен тем, что это не проблема, которую может наблюдать любой пользователь программы на любой платформе.

4b9b3361

Ответ 1

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

public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws Exception {
        Runtime.getRuntime().exec(args[0]);
    }
}

И протестирован на Linux:

java -jar JustForTesting.jar /home/monceaux/Desktop/__TMP/test.sh

где test.sh выглядит так:

#!/bin/bash
ping -i 20 localhost

а также в Linux:

java -jar JustForTesting.jar gedit

И протестировал это в Windows:

java -jar JustForTesting.jar notepad.exe

Все они запустили свои намеченные программы, но у приложения Java не было проблем с выходом. У меня есть следующие версии Sun JVM, о которых сообщает java -version:

  • Windows: 1.6.0_13-b03
  • Linux: 1.6.0_10-b33

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

Ответ 2

Между вашими процессами существует родительское дочернее отношение, и вам нужно это сломать. Для Windows вы можете попробовать:

Runtime.getRuntime().exec("cmd /c start editor.exe");

Для Linux процесс, похоже, все равно отключается, nohup необходимо. Я пробовал его с помощью gvim, midori и acroread.

import java.io.IOException;
public class Exec {
    public static void main(String[] args) {
        try {
            Runtime.getRuntime().exec("/usr/bin/acroread");
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("Finished");
    }
}

Я думаю, что это невозможно сделать с Runtime.exec независимым от платформы способом.

для POSIX-совместимой системы:

 Runtime.getRuntime().exec(new String[]{"/bin/sh", "-c", "your command"}).waitFor();

Ответ 3

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

Когда вы используете Runtime.getRuntime(). exec(), а затем игнорируете дескриптор java.lang.Process, который вы возвращаете (например, в коде с оригинального плаката), есть вероятность, что запущенный процесс может зависать.

Я столкнулся с этой проблемой в среде Windows и проследил проблему с потоками stdout и stderr. Если запущенное приложение записывает эти потоки, и буфер для этого потока заполняется, тогда запущенное приложение может оказаться зависающим, когда оно пытается выполнить запись в потоки. Решения:

  • Захват дескриптора процесса и периодическое удаление потоков - но если вы хотите прервать Java-приложение сразу после запуска процесса, это не выполнимое решение.
  • Выполните вызов процесса как 'cmd/c < > ' (это только для среды Windows).
  • Суффикс команды процесса и перенаправление потоков stdout и stderr в nul с помощью команды "nul 2 > & 1"

Ответ 4

Вы хотите запустить программу в фоновом режиме и отделить ее от родителя. Я бы рассмотрел nohup (1).

Ответ 5

Я подозреваю, что для этого потребуется фактическая вилка процесса. В принципе, C эквивалент того, что вы хотите:

pid_t id = fork();
if(id == 0)
  system(command_line);

Проблема в том, что вы не можете сделать fork() в чистой Java. Я бы сделал следующее:

Thread t = new Thread(new Runnable()
{
    public void run()
    {
      try
      {
          Runtime.getRuntime().exec(command);
      }
      catch(IOException e)
      {           
          // Handle error.
          e.printStackTrace();
      }
    }
});
t.start();

Таким образом, JVM все равно не выйдет, но графический интерфейс и ограниченный объем памяти не останется.

Ответ 6

Один из способов, о котором я могу думать, - использовать Runtime.addShutdownHook для регистрации потока, который уничтожает все процессы (вам нужно было бы сохранить объекты процесса где-то, конечно).

Крюк выключения вызывается только при выходе из JVM, поэтому он должен работать нормально.

Немного взлома, но эффективный.