Передача и прием сигналов
Один из вариантов организации межпроцессного взаимодействия осно-ван на передаче и приеме сигналов. Сигнал — зто одноразрядное сообщение (означающее, что "произошло данное событие"), которое посылается в процесе из другого процесса или из ядра. Сигналам присваиваются номера, обычно из диапазона от единицы до небольшого числа, например 15 или 31. Одни сигналы (фиксированные) имеют предопределенное значение и посы-лаются в процесе автоматически при возникновении определенных обстоя-тельств (например, при сбоях памяти или в исключительных ситуациях, возникающих при выполнении операций с плавающей запятой). Другие сигналы генерируются исключительно пользователем из других процессов, но не из всех, а только их тех, которые имеют разрешение на передачу сигналов. Передача сигнала разрешается только в том случае, если вы являєтесь привилегированным пользователем или если передающий сигнал процесе имеет тот же идентификатор пользователя, что и принимающий.
Ответ на сигнал называется действием
сигнала. Фиксированные сигналы выполняют определенные действия по умолчанию, например, осуществляют прерывание или приостановку процесса. Остальные сигналы по умолчанию
* Полезно таюке знать о формах типа open(STDERR, ">&stdout") , используемых для точной настройки дескрипторов файлов. См. пункт open в главе 3 книги Programming Perl или на man-странице per1func(l).
полностью игнорируются. Почти для любого сигнала действие по умолчанию может быть переопределено, с тем чтобы данный сигнал либо игнорировал-ся, либо перехватывался (с автоматическим вызовом указанной пользовате-лем части кода).
Все зто аналогично тому, что делается и в других языках программиро-вания, но сейчас излагаемый материал приобретет специфический Perl-от-тенок. Когда Perl-процесе перехватывает сигнал, асинхронне и автоматиче-ски вызывается указанная вами подпрограмма, моментально прерывая выполнявшийся до нее код. Когда зта подпрограмма завершается, вьшолне-ние прерванного кода возобновляется, как будто ничего не случилось (за исключением появлення результатов действий, внполненных зтой подпро-граммой,— если она вообще что-нибудь делала).
Обычно подпрограмма- обработчик сигнала делает одно из двух: преры-вает программу, выполнив "очистку", или устанавливает какой-то флаг (например, глобальную переменную), которую данная программа затем проверяет*.
Для того чтобы зарегистрировать подпрограммы-обработчики сигналов в Perl, нужно знать имена сигналов. После регистрации обработчика сигнала Perl при получении зтого сигнала будет вызывать выбранную подпрограмму.
Имена сигналов определяются на man-странице signal(2), а также, как правило, в подключаемом С-файле /usr/include/sys/signal.h.
Зти имена обычно начинаются с букв sig, например sigint, sigquit и sigkill. Чтобы обьявить подпрограмму my_sigint_catcher () обработчиком сигнала sigint, мы должны установить соответствующее значение в специальном хеше %sig. В зтом хеше в качестве значення ключа int (зто sigint без sig) следует указать имя подпрограммы, которая будет перехватывать сигнал sigint:
$SIG{'INT'} ” 'my_sigint_catcher';
Но нам понадобится также определение зтой подпрограммы. Вот пример простого определения:
sub my_sigint_catcher (
$saw_sigint = 1; # установить флаг }
Данный перехватчик сигналов устанавливает глобальную переменную и сразу же возвращает управление. Выполнение программы продолжается с той позиции, в которой оно было прервано. Обычно сначала обнуляется флаг $saw_sigint, соответствующая подпрограмма определяется как перехватчик сигнала sigint, а затем следует код основной программы, например:
$saw_sigint = 0; # очистить флаг $SIG{'INT') = 'my_sigint_catcher'; # зарегистрировать перехватчик
foreach (@huge_array) (
# что-нибудь сделать
t
еще что-нибудь сделать
# и еще что-нибудь if ($saw_sigint) ( # прерывание нужно?
t здесь "очистка" last;
} } $SIG('INT'} = 'DEFAULT'; # восстановить действие по умолчанию
* Попытка вьшолнения действий более сложных, чем вышеописанные, вероятнее всего, запутает ситуацию; большинство внутренних механизмов Perl "не любят", когда их вызы-вают одновременно в основной программе и из подпрограммы. Ваши системныс библио-теки тоже зтого "не любят".
Особенность использования сигнала в данном фрагменте программы состоит том, что значение флага проверяется в важнейших точках процесса вычисления и используется для преждевременного выхода из цикла; при зтом выполняется и необходимая "очистка". Обратите внимание на послед-ний оператор в приведенном выше коде: установка действия в значение default восстанавливает действие конкретного сигнала по умолчанию (следующий сигнал sigint немедленно прервет вьшолнение программы). Еще одно полезное специальное значение вроде зтого — ignore, т.е. "игнорировать сигнал" (если действие по умолчанию — не игнорировать сигнал, каку sigint). Для сигнала можно установить действие ignore, если не нужно виполнять никакой "очистки" и вы не хотите преждевременно завершать выполнение основной программы.
Один из способов генерирования сигнала sigint — заставить пользователя нажать на клавиатуре терминала соответствующие прерыванию клавиши (на-пример, [Ctrl+C]). Процесе тоже может генерировать сигнал sigint, используя для зтого функцию kill. Данная функция получает номер или имя сигнала и посылает соответствующий сигнал в процессы (обозначенные идентификато-рами) согласно списку, указанному после сигнала. Следовательно, для передачи сигнала из программы необходимо определить идентификаторы процессов-по-лучателей. (Идентификаторы процессов возвращаются некоторыми функция-ми, например функцией fork, и при открьггии программы как дескриптора файла функцией open). Предположим, вы хотите послать сигнал 2 (известный также как sigint) в процессы 234 и 237. Зто делается очень просто:
kill(2,234,237); # послать SIGINT в 234 и 237 kill ('INT', 234, 237); # то же самое
Более подробно вопросы обработки сигналов описаны в главе 6 книги Programming Perl и на man-странице perlipc(l).