Что происходит с SIGINT (^ C) при отправке в сценарий perl, содержащий дочерние элементы? - PullRequest
13 голосов
/ 17 января 2011

У меня есть Perl-скрипт, который разветвляется.

Каждый форк запускает внешнюю программу, анализирует выходные данные и преобразует выходные данные в сохраняемый файл.

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

Что именно происходит, когда я запускаю ^ C, когда некоторые дети все еще работают с внешней программой? Родительский сценарий perl был вызван на переднем плане и, как я полагаю, остался на переднем плане, несмотря на разветвление.

Передается ли SIGINT всем потомкам, то есть родителю, потомкам родителя и внешней программе, вызываемой потомками ??

UPDATE

Я должен добавить, что, похоже, когда я запускаю SIGINIT, внешняя программа, вызываемая дочерними элементами моего скрипта, похоже, подтверждает сигнал и завершает работу. Но дети, или, возможно, родительская программа, продолжают. Это все неясно для меня.

ОБНОВЛЕНИЕ 2 :

Что касается комментария tchrist, внешняя программа вызывается командой Perl system().

Фактически, комментарий Триста также, кажется, содержит объяснение, которое я искал. После некоторой дополнительной отладки, основанной на поведении моей программы, выясняется, что действительно SIGINT передается от родителя всем детям и от всех детей всем их детям (внешняя программа).

Таким образом, похоже, что, исходя из комментариев Триста, происходит то, что CTRL-C убивает внешнюю программу, в результате чего дети выходят из команды system() - и ничего более.

Хотя мои дети проверяли состояние выхода того, что было вызвано в system(), я предполагал, что CTRL-C уничтожит все, начиная с родительского, а не приведет к созданию большего количества циклов обработки, что это то, что происходило !!!

РЕШЕНИЕ (к моей проблеме) :

Мне нужно просто создать обработчик сигнала для SIGINT в родительском. Затем обработчик сигнала посылает SIGTERM каждому из дочерних элементов (который, как я полагаю, также отправляет SIGTERM дочерним элементам), а затем заставляет родительский элемент корректно завершить работу. Хотя это несколько очевидное решение, скорее всего, могло бы исправить ситуацию, я хотел понять свое неправильное представление о поведении SIGINT относительно разветвления в Perl.

Ответы [ 3 ]

13 голосов
/ 18 января 2011

Встроенная в Perl функция system работает так же, как и функция C system (3) из стандартной библиотеки C, что касается сигналов.Если вы используете версию Perl system() или pipe open или backticks, то родительский объект - вызывающий system, а не вызываемый им - будет игнорировать SIGINT и SIGQUIT во время работы дочерних элементов.Если вы использовали свой вариант трио fork?wait:exec, то вы должны подумать об этом самостоятельно.

Рассмотрим, что происходит, когда вы используете system("vi somefile") и нажимаете ^ C во время длительного поиска в vi: только vi принимает (нефатальный) SIGINT;родитель игнорирует это.Это правильное поведение.Вот почему C работает именно так, и именно поэтому Perl работает именно так.

Вам нужно помнить, что только потому, что ^ C отправляет SIGINT всем процессам в группе процессов, созданных ранее (даже если они отличаются)эффективный UID или GID), который не означает, что он вызывает завершение всех этих процессов.^ C - это только SIGINT, предназначенный для прерывания процесса, а не SIGKILL, предназначенный для завершения без вопросов.

Существует множество программ, которые было бы неправильно просто убить без предупреждения;редактор - только один такой пример.Почтовик может быть другим.Будьте чрезвычайно осторожны с этим.

Многие виды программ выборочно игнорируют, перехватывают или блокируют (означает задержку доставки) различные виды сигналов.Только поведение SIGINT по умолчанию - запуск процесса.Вы можете узнать, произошло ли это, и на самом деле какой сигнал вызвал это (среди прочего), с помощью такого рода кода в традиционных операционных системах:

if ($wait_status = system("whatever")) {
    $sig_killed   = $wait_status & 127;
    $did_coredump = $wait_status & 128;
    $exit_status  = $wait_status >>  8;
    # now do something based on that...
}

Обратите внимание, что ^ C'dvi, например, не будет иметь слово состояния ожидания, указывающее, что оно умерло от незадействованного SIGINT, так как его не было: он его поймал.

Иногда ваши дети уходяти иметь своих детей за спиной.Грязно, но верно.Поэтому иногда мне известно, что геноцид всех потомков , известных и неизвестных , таким образом:

# scope to temporize (save+restore) any previous value of $SIG{HUP}
{
    local $SIG{HUP} = "IGNORE";
    kill HUP => -$$;   # the killpg(getpid(), SIGHUP) syscall
}

Это, конечно, не работает с SIGKILL или SIGSTOP, которые не поддаютсячтобы меня так игнорировали.

Еще один вопрос, о котором вы могли бы позаботиться, это то, что до выхода 5.8 обработка сигналов в Perl исторически не была надежно безопасной операцией.Сейчас , но это зависит от версии.Если вы еще этого не сделали, то вам непременно следует прочитать о отложенных сигналах в perlipc manpage и, возможно, также о PERL_SIGNALS, доступном в perlrun manpage .

9 голосов
/ 17 января 2011

Когда вы нажимаете ^ C в окне терминала (или в любом другом терминале), он отправляет сигнал SIGINT на передний план процесса GROUP в этом терминале. Теперь, когда вы запускаете программу из командной строки, она обычно находится в своей собственной группе процессов, и она становится приоритетной группой процессов. По умолчанию, когда вы разветвляете дочерний элемент, он будет в той же группе процессов, что и родительский элемент, поэтому по умолчанию родительский элемент (программа верхнего уровня, вызываемая из командной строки), все его дочерние элементы, дочерние дочерние элементы и т. Д., А также все внешние программы, вызванные любой из них (которые все являются только дочерними), будут все в той же группе процессов, поэтому все будут получать сигнал SIGINT.

Однако, если какой-либо из этих потомков или программ вызывает setpgrp или setpgid или setsid, или любой другой вызов, который заставляет процесс быть в новой группе прогресса, эти процессы (и любые дочерние элементы они запускают после выхода из группы процессов переднего плана). ) не получит SIGINT.

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

редактировать

Из вашего обновления звучит так, как будто все остается в одной и той же группе процессов, поэтому сигналы доставляются всем, так как выходят внуки (внешние программы), но дети ловят и игнорируют SIGINT. Согласно комментарию tchrist, это выглядит так, как будто это поведение по умолчанию для perl.

2 голосов
/ 17 января 2011

Если вы убьете родительский процесс, дочерние элементы (и запущенные внешние программы) будут работать до тех пор, пока они не завершатся тем или иным способом.Этого можно избежать, если у родителя есть обработчик сигнала, который перехватывает SIGINT, а затем убивает группу процессов (часто pid родителя).Таким образом, как только родитель получит SIGINT, он убьет всех своих потомков.

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

...