Что происходит, когда процесс разветвляется?

Я читал о форке и, насколько я понимаю, процесс клонирован, но какой процесс? Сам скрипт или процесс, запустивший скрипт?

Например:

Я запускаю rTorrent на своей машине, и когда торрент завершается, у меня запускается скрипт. Этот скрипт извлекает данные из Интернета, поэтому его выполнение занимает несколько секунд. На это время мой rtorrent процесс завис. Итак, я сделал форк скрипта, используя следующее

my $pid = fork();
if ($pid == 0) { blah blah blah; exit 0; }

Если я запускаю этот скрипт из CLI, он возвращается в оболочку в течение секунды, пока он работает в фоновом режиме, как я и предполагал. Однако, когда я запускаю его из rTorrent, он кажется еще медленнее, чем раньше. Так что именно было разветвлено? Процесс rtorrent клонировал себя, и мой скрипт запускался в нем, или мой скрипт клонировал себя? Я надеюсь это имеет смысл.


person somebody    schedule 07.03.2010    source источник
comment
Пожалуйста, начните с публикации работающего фрагмента Perl.   -  person Leon Timmermans    schedule 07.03.2010
comment
Попробуйте запустить rTorrent в strace и посмотрите, что он блокирует, когда работает ваш скрипт. Это может дать подсказку. Я думал, что, возможно, это было ожидание () в дочернем процессе, но кажется, что такое поведение на самом деле невозможно с использованием традиционных системных вызовов.   -  person jdizzle    schedule 07.03.2010


Ответы (6)


Чтобы ответить на номинальный вопрос, поскольку вы отметили, что принятый ответ не соответствует этому, fork влияет на процесс, в котором он вызывается. В вашем примере rTorrent порождает процесс Perl, который затем вызывает fork, это процесс Perl, который дублируется, поскольку именно процесс Perl вызвал fork.

В общем случае у процесса нет возможности fork использовать любой другой процесс, кроме самого себя. Если бы можно было указать другому произвольному процессу идти fork самому, это открыло бы безграничные проблемы с безопасностью и производительностью.

person Dave Sherohman    schedule 07.03.2010
comment
Помимо того, что открывается возможность для множества шуток: эй, ты! иди раскошелишься! нет, вилка тебя! - person Ether; 07.03.2010

Функция fork() возвращает ДВАЖДЫ! Один раз в родительском процессе и один раз в дочернем процессе. В общем, оба процесса ИДЕНТИЧНЫ во всем, как будто КАЖДЫЙ только что вернулся из fork(). Разница только в том, что в одном из fork() возвращается значение 0, а в другом оно не равно нулю (PID дочернего процесса).

Таким образом, какой бы процесс ни запускал ваш Perl-скрипт (если это встроенный интерпретатор Perl внутри rTorrent, тогда rTorrent будет процессом) будет дублироваться именно в тот момент, когда произошло fork().

person Adam Batkin    schedule 07.03.2010
comment
@jdizzle - Вероятно, потому, что вопрос не имеет особого смысла, потому что somebody не понимает процесс и разветвление идей. Объяснение некоторых фактов может помочь :) - person viraptor; 07.03.2010
comment
@viraptor - я чувствую, что кто-то достаточно хорошо разбирается в fork()ing. Вопрос действительно о реализации rTorrent. - person jdizzle; 07.03.2010
comment
Вопрос спрашивает, какой из них клонирован, и ответом является любой процесс, в котором работает fork() (плюс некоторое дополнительное объяснение того, как работает fork(), которое должно помочь понять, почему все это происходит именно так) - person Adam Batkin; 07.03.2010

Я считаю, что нашел проблему, просмотрев исходный код rTorrent. Для некоторых процессов он будет считывать все выходные данные, отправленные на стандартный вывод, прежде чем продолжить. Если это происходит с вашим процессом, rTorrent будет блокироваться до тех пор, пока вы не закроете процесс stdout. Поскольку вы разветвляете, ваш дочерний процесс использует тот же стандартный вывод, что и родительский. Ваш родительский процесс завершится, но канал останется открытым (поскольку ваш дочерний процесс все еще работает). Если бы вы использовали rTorrent, я бы поспорил, что он будет заблокирован на этом read() вызове во время выполнения вашей команды.

Попробуйте закрыть/перенаправить стандартный вывод в вашем perl-скрипте до того, как fork().

person jdizzle    schedule 07.03.2010
comment
Решает проблему, но не отвечает на номинальный вопрос. Я хотел бы, что. - person darch; 07.03.2010
comment
@darch - название вопроса на самом деле имеет отношение к проблеме, которую кто-то пытается решить - person jdizzle; 07.03.2010
comment
Я нашел это очень полезным, так что спасибо за это, хотя это не ответило на номинальный вопрос, @jdizzle :) - person Jorge Israel Peña; 27.12.2011
comment
Возможно, XMLRPC API изменился с тех пор, как вы написали это, но изучение rpc/exec_file.cc, похоже, показывает (L: 112), что захват stdout происходит только при использовании exec_capture. В противном случае он пропускает эту часть кода. Проблема, похоже, в том, что в конце он делает waitpid до тех пор, пока не перестанет возвращать -1. Существует встроенный способ пропустить это, передав флаг flag_background, который отображается внутри как execute.throw.bg, но, к сожалению, он не отображается снаружи как часть API XMLRPC, обращенного наружу. - person Jorge Israel Peña; 27.12.2011
comment
exec_capture должно было быть execute_capture выше. - person Jorge Israel Peña; 27.12.2011

Весь процесс, содержащий интерпретатор, разветвляется. К счастью, память копируется при записи, поэтому для разветвления не нужно копировать всю память процесса. Однако такие вещи, как файловые дескрипторы, остаются открытыми. Это позволяет дочерним процессам обрабатывать их, но может вызвать проблемы, если они не закрыты должным образом. В общем, fork() не следует использовать во встроенном интерпретаторе, кроме как в крайнем случае.

person Ignacio Vazquez-Abrams    schedule 07.03.2010
comment
Мех. Это не конец света для fork() в Perl на машине конечного пользователя. Я согласен с тем, что частое использование, вероятно, является плохой практикой (поскольку это созрело для узкого места). - person jdizzle; 07.03.2010
comment
Если это плохая практика, есть ли альтернативный метод предотвращения блокировки? - person somebody; 07.03.2010

Мой совет будет "не делай этого".

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

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

person Nicholas Knight    schedule 07.03.2010
comment
Насколько распространено фактическое связывание с интерпретатором Perl? Не было бы намного практичнее (и безопаснее) использовать system() для таких вызовов? - person jdizzle; 07.03.2010
comment
Правда, вызов fork() в многопоточной программе напрашивается на неприятности. Однако, если вы ограничиваете то, что происходит в дочернем процессе, это не так уж плохо. Например, не вызывайте ничего, что требует блокировки пользовательского режима. Но типичное использование следующего за fork() с dup2(), close(), execve() и т. д. должно быть безопасным. - person asveikau; 07.03.2010
comment
@jdizzle: Связывание с внешним интерпретатором, Perl или другим, очень распространено, но из вопроса не совсем ясно, так ли это в случае с этой программой. Однако при повторном прочтении вы можете быть правы в том, что используется system(). - person Nicholas Knight; 07.03.2010
comment
Это не встроенный интерпретатор. Я запускаю внешний скрипт, так что это может быть perl, python, что угодно. - person somebody; 07.03.2010

Когда мы создаем процесс с помощью fork, дочерний процесс будет иметь копию адресного пространства. Таким образом, дочерний процесс также может использовать адресное пространство. И он также может получить доступ к файлам, которые открыты родителем. Мы можем контролировать child. Чтобы получить полный статус дочернего элемента, мы можем использовать ожидание.

person karthi_ms    schedule 08.03.2010