Что происходит с SIGINT (^ C) при отправке в perl script с детьми?
У меня есть Perl script, который вилки.
Каждая вилка запускает внешнюю программу, анализирует вывод и преобразует вывод в файл Storable.
Затем хранящиеся файлы считываются родителем, а общие данные из каждого из них анализируются перед повторением предыдущей вилки, иначе родительский останавливается.
Что именно происходит, когда я выдаю a ^ C, а некоторые из детей все еще запускают внешнюю программу? Родительский perl script был вызван на переднем плане и, я полагаю, остался на переднем плане, несмотря на разветвление.
Передано ли SIGINT всем дочерним элементам, то есть родительским, родительским дочерним элементам и внешней программе, вызываемым дочерними элементами.
UPDATE
Я должен добавить, похоже, что когда я выдаю SIGINIT, внешняя программа, вызываемая дочерними элементами моего script, кажется, подтверждает сигнал и завершает работу. Но дети, или, возможно, родительская программа, продолжаются. Для меня это все непонятно.
ОБНОВЛЕНИЕ 2:
В отношении комментария tchrist внешняя программа вызывается с помощью команды Perl system()
.
На самом деле, комментарий tchrist также, похоже, содержит объяснение, которое я искал. После некоторой дополнительной отладки, основанной на поведении моей программы, кажется, что SIGINT передается от родителя ко всем детям и от всех детей ко всем их детям (внешняя программа).
Таким образом, то, что, по-видимому, происходит на основе комментария tchrist, заключается в том, что CTRL-C убивает внешнюю программу, которая заставляет детей перемещаться из команды system()
- и не более того.
Хотя мои дети проверяли статус выхода из того, что было вызвано в system()
, я предполагал, что CTRL-C убьет все от родителя, а не приведет к созданию большего количества раундов обработки, что это то, что происходило!!!
РЕШЕНИЕ (к моей проблеме):
Мне нужно просто создать обработчик сигнала для SIGINT в родительском. Обработчик сигнала затем отправит SIGTERM каждому из дочерних элементов (который, как я полагаю, также отправит SIGTERM детям-детям), а затем заставит родителя выйти изящно. Хотя это несколько очевидное решение, вероятно, фиксировало бы вещи, я хотел понять мое заблуждение о поведении SIGINT в отношении разветвления на Perl.
Ответы
Ответ 1
Функция встроенного system
Perls работает так же, как функция C (3) из стандартной библиотеки C в отношении сигналов. Если вы используете Perls-версию system()
или open или backticks для труб, то родительский - тот, кто вызывает system
, а не тот, который его вызвал, будет IGNORE любыми SIGINT и SIGQUIT во время работы детей. Если вы перекатили свой собственный, используя какой-либо вариант трио fork?wait:exec
, тогда вы должны сами об этом подумать.
Посмотрите, что произойдет, когда вы используете system("vi somefile")
и нажмите ^ C во время длительного поиска в vi
: только vi
принимает (нефатальный) SIGINT; родитель игнорирует его. Это правильное поведение. Вот почему C работает таким образом, и поэтому Perl работает таким образом.
Вы должны помнить, что только потому, что a ^ C отправляет SIGINT ко всем процессам в группе процессов (даже тех, которые отличаются от UID или GID), что означает не, означает, что это приводит к выходу всех этих процессов. A ^ 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...
}
Обратите внимание, что a ^ Cd vi
, например, будет не иметь слово состояния ожидания, указывающее, что оно умерло от незапечатанного 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, которые не поддаются IGNOREd.
Другое дело, которое вы, возможно, захотите быть осторожным, заключается в том, что перед выпуском версии 5.8 обработка сигналов в Perl исторически не была надежной безопасностью. Теперь он , но это проблема, зависящая от версии. Если вы еще не сделали этого, то вам обязательно нужно прочитать на отложенные сигналы в man-странице perlipc и, возможно, также на PERL_SIGNALS
envariable в man-странице perlrun.
Ответ 2
Когда вы нажмете ^ C в окне терминала (или на любом терминале, если на то пошло), он отправит SIGINT в GROUP процесса переднего плана в этом терминале. Теперь, когда вы запускаете программу из командной строки, она обычно находится в собственной группе процессов и становится группой процессов переднего плана. По умолчанию, когда вы разыгрываете дочерний элемент, он будет в той же группе процессов, что и родительский, поэтому по умолчанию родительский (программа верхнего уровня, вызванная из командной строки), все его дети, дети-дети и т.д., А также любые внешние программы, вызываемые любым из них (которые являются всего лишь детьми), будут находиться в той же самой группе процессов, поэтому все получат сигнал SIGINT.
Однако, если какой-либо из этих детей или программ вызывает setpgrp или setpgid или setid или любой другой вызов, который заставляет процесс быть в новой группе прогресса, эти процессы (и любые дети, которые они запускают после выхода из группы процессов переднего плана ) НЕ получит SIGINT.
Кроме того, когда процесс получает SIGINT, он может не заканчиваться - он может игнорировать сигнал, или он может поймать его и сделать что-то совершенно другое. Обработчик сигнала по умолчанию для SIGINT завершает процесс, но это только значение по умолчанию, которое можно переопределить.
изменить
Из вашего обновления кажется, что все осталось в одной группе процессов, поэтому сигналы доставляются всем, так как внуки (внешние программы) выходят, но дети ловят и игнорируют SIGINT. По комментарию tchrist, похоже, что это поведение по умолчанию для perl.
Ответ 3
Если вы убьете родительский процесс, дети (и внешние запущенные программы) будут работать до тех пор, пока они не прекратятся так или иначе. Этого можно избежать, если у родителя есть обработчик сигнала, который ловит SIGINT, а затем убивает группу процессов (часто родительский pid). Таким образом, как только родитель получит SIGINT, он убьет всех его детей.
Итак, чтобы ответить на ваш вопрос, все зависит от реализации обработчика сигнала. Основываясь на вашем обновлении, кажется, что родитель действительно убивает его детей, а затем вместо того, чтобы завершать себя, он возвращается к выполнению чего-то другого (подумайте об этом как о reset вместо полной комбинации shutdown/start).