2009-04-30 3 views
22

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

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

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


Итак, это путь, или как еще я должен сделать это?

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

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


Я оценил бы какое-то объяснение о том, как fork работ. [получил это уже]

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

ответ

20

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

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

Следовательно:

exec("something") if fork.nil? 

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

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

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

+1

Хорошо, я думаю, что получил .nil? проверить. В ребенке он действительно не возвращает 0, он возвращает nil. Таким образом, exec будет выполняться, если вы находитесь у ребенка. Итак, как бы я захватил другой возврат вилки, тот, который возвращает идентификатор дочернего процесса? Таким образом, я мог бы использовать его напрямую, вместо того, чтобы полагаться на $$. – kch

+2

просто вызовите fork и запишите результат. то, если вы получили нуль, выполните exec, иначе вы находитесь в родительском. – Alnitak

+0

Это такое яркое и четкое определение «вилка». Отличная работа, спасибо. – KomodoDave

28

Alnitak is right. Вот более явный способ записать его без $$

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

Цель отсоединение просто сказать: «Я не забочусь, когда ребенок заканчивает», чтобы избежать zombie processes.

+1

Привет, я играл с моими новыми знаниями, и я в основном пришел к более сжатой версии того же: (pid = fork)? Process.detach (pid): exec ("foo") – kch

+0

Да. Это работает. –

+0

@MatthewFlaschen В случае, если это не содержит 'exec', мне не нужен« exit »в дочернем коде для процесса зомби, который не продлится? – oldergod

2

Отсоединение $$ не соответствует. С. 348 of Pickaxe (2-е изд.):

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

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

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

+0

Что-то устроило ваш ответ. Во всяком случае, у меня есть третье издание кирки. Итак, $$ не прав, факт. Но почему это все равно работает? – kch

+2

Это не работает правильно, если вы вызываете detach на $$. Но опять же, отсоединить только, чтобы избежать зомбических процессов. Нет необходимости делать работу в фоновом режиме. –

2

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

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

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

0

Я нашел ответы выше сломал мой терминал и испортил выход. это решение, которое я нашел.

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 
Смежные вопросы