Отправка и прием сигналов

Сигнал UNIX представляет собой крошечное сообщение, отправленное процессу. Он не может содержать подробной информации; представьте себе автомобильный сигнал – что он может означать? «Осторожно, здесь обвалился мост», или «Светофор переключился, можно ехать», или «Остановись, у тебя на крыше ребенок», или «Привет всем»? К счастью, понять смысл сигнала UNIX несколько проще, потому что в каждой из описанных ситуаций используется свой сигнал. Сигналы идентифицируются по именам (например, SIGINT – «сигнал прерывания») и коротким числовым кодам (в диапазоне от 1 до 16, от 1 до 32 или от 1 до 63 в зависимости от вашей разновидности UNIX). Сигнал обычно отправляется при обнаружении важного события. Например, при нажатии клавиш прерывания программы (чаще всего ControlQC) на терминале всем процессам, присоединенным к этому терминалу, отправляется сигнал SIGINT. Некоторые сигналы автоматически отправляются системой, но они также могут отправляться другими процессами.

Вы можете отправить из процесса Perl сигнал другому процессу, но для этого необходимо знать идентификатор целевого процесса. Определить его не так просто3, но предположим, вы хотите отправить сигнал SIGINT процессу 4201.


Делается это достаточно просто:

kill 2, 4201 or die "Cannot signal 4201 with SIGINT: $!";

Функция называется kill, потому что сигналы очень часто применяются для остановки процесса, выполнение которого занимает слишком много времени. Вместо 2 также можно использовать строку 'INT', так как числовой код 2 соответствует сигналу SIGINT. Если процесс не существует4, вы получите значение false, поэтому этот способ позволяет проверить, жив ли процесс. Специальный сигнал с номером 0 означает примерно следующее: «Просто проверить, смогу ли я отправить сигнал, если захочу… Но я пока не хочу, так что и отправлять ничего не нужно». Таким образом, зондирование процесса может выглядеть примерно так:

unless (kill 0, $pid) {
warn "$pid has gone away!";
}

Пожалуй, принимать сигналы немного интереснее, чем отправлять их. Зачем это может быть нужно? Допустим, ваша программа создает временные файлы в каталоге /tmp, которые удаляются в конце работы программы. Если ктоQто нажмет ControlQC во время выполнения, при аварийном завершении в /tmp останется «мусор», а это крайне невежливо. Проблема решается обработчиком сигнала, который позаботится об очистке:

my $temp_directory = "/tmp/myprog.$$"; # Каталог для временных файлов
mkdir $temp_directory, 0700 or die "Cannot create $temp_directory: $!";
sub clean_up {
unlink glob "$temp_directory/*";
rmdir $temp_directory;
}
sub my_int_handler {
&clean_up;
die "interrupted, exiting...\n";
}
$SIG{'INT'} = 'my_int_handler';
.
.


# Время идет, программа работает, в каталоге создаются
. # временные файлы. Потом кто-то нажимает Control-C.
.
# Конец нормального выполнения
&clean_up;

Присваивание элементу специального хеша %SIG активизирует обработчик сигнала (пока он не будет снова отменен). Ключом является имя сигнала (без префикса SIG), а значением – строка1 с именем функции (без знака &). В дальнейшем при получении сигнала SIGINT Perl немедленно прерывает свою текущую работу и передает управление заданной функции. Функция удаляет временные файлы, а затем завершает работу программы. Если никто не нажал Control-C, функция &clean_up все равно будет вызвана в конце нормального выполнения программы. Если функция возвращает управление вместо вызова die, выполнение продолжается с того места, на котором оно было прервано. Это может быть полезно, если сигнал должен прервать какую-то операцию без завершения всей программы. Допустим, обработка каждой строки файла занимает несколько секунд, и вы хотите отменить общую обработку при получении сигнала прерывания, но только не на середине обработки строки. Установите флаг в обработчике прерывания и проверяйте его состояние в конце обработки каждой строки:

my $int_count;
sub my_int_handler { $int_count++ }
$SIG{'INT'} = 'my_int_handler';
...
$int_count = 0;
while () {
...


Обработка в течение нескольких секунд ...
if ($int_count) {
# Получен сигнал прерывания !
print "[processing interrupted...]\n";
last;
}
}

Во время обработки каждой строки значение $int_count равно 0. Если никто не нажал Control-C, цикл переходит к следующей строке. Но при поступлении сигнала прерывания обработчик сигнала увеличивает флаг $int_count, а это приводит к выходу из цикла при завершающей проверке. Итак, при получении сигнала можно либо установить флаг, либо завершить работу программы; собственно, на этом возможности обработки сигналов заканчиваются. Впрочем, текущая реализация обработчиков сигналов неидеальна1; постарайтесь свести объем выполняемых действий к минимуму, или ваша программа может «упасть» в самый неподходящий момент.

Оцените статью: (0 голосов)
0 5 0

Статьи из раздела Perl на эту тему:
Ветвление
Выполнение команд в обход командного процессора
Обратные апострофы в списочном контексте
Обратные апострофы и сохранение вывода
Переменные среды

Вернуться в раздел: Perl / 15. Управление процессами