Как запустить и забыть подпроцесс? - PullRequest
22 голосов
/ 30 апреля 2009

У меня длительный процесс, и он мне нужен для запуска другого процесса (он тоже будет работать долго). Мне нужно только запустить его, а потом полностью забыть об этом.

Мне удалось сделать то, что мне было нужно, выкопав некоторый код из книги по программированию на Ruby, но я бы хотел найти лучший / правильный путь и понять, что происходит. Вот что я получил изначально:

exec("whatever --take-very-long") if fork.nil?
Process.detach($$)


Так вот, или как мне еще это сделать?

После проверки ответов ниже я получил этот код, который, кажется, имеет больше смысла:

(pid = fork) ? Process.detach(pid) : exec("foo")


Буду признателен за объяснение того, как работает fork. [получил это уже]

Был ли отрыв $$ верно? Я не знаю, почему это работает, и я бы очень хотел, чтобы лучше понять ситуацию.

Ответы [ 5 ]

31 голосов
/ 30 апреля 2009

Альнитак прав. Вот более явный способ написать это без $$

pid = Process.fork
if pid.nil? then
  # In child
  exec "whatever --take-very-long"
else
  # In parent
  Process.detach(pid)
end

Цель отсоединения состоит в том, чтобы просто сказать: «Мне все равно, когда ребенок заканчивает», чтобы избежать процессов зомби .

20 голосов
/ 30 апреля 2009

Функция fork разделяет ваш процесс на две части.

Оба процесса затем получают результат функции. Ребенок получает значение ноль / nil (и, следовательно, знает, что это ребенок), а родитель получает PID ребенка.

Таким образом:

exec("something") if fork.nil?

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

Обратите внимание, что exec() заменяет текущего процесса на "что-то", поэтому дочерний процесс никогда не выполнит любой последующий код Ruby.

Вызов Process.detach() выглядит так, как будто он неверный. Я ожидал, что в нем есть PID ребенка, но если я правильно прочитал ваш код, это фактически отсоединяет родительский процесс.

2 голосов
/ 14 июля 2013

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

fork do
    exec('whatever --option-flag')
end

Предоставление блока указывает форку выполнить этот блок (и только этот блок) в дочернем процессе, продолжая при этом в родительском процессе.

2 голосов
/ 30 апреля 2009

Отделение $$ было не правильно. Из с. Кирка 348 (2-е изд):

$$ Fixnum Номер процесса выполняемой программы. [r / o]

Этот раздел «Переменные и константы» в главе «Язык Ruby» очень удобен для декодирования различных констант ruby ​​short $ - однако, онлайн-издание (первое

Итак, что вы на самом деле делали, так это отключали программу от самой себя, а не от ее дочернего элемента. Как уже говорили другие, правильный способ оторваться от ребенка - это использовать пид ребенка, возвращенный с fork().

0 голосов
/ 29 июня 2016

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

   system("nohup ./test.sh &")

на тот случай, если у кого-то возникнет такая же проблема, моя цель состояла в том, чтобы войти на сервер ssh и затем поддерживать этот процесс в течение неопределенного времени. так что test.sh это

#!/usr/bin/expect -f
spawn ssh host -l admin -i cloudkey  
expect "pass"
send "superpass\r"
sleep 1000000
...