четверг, 18 декабря 2008 г.

Обработка сигнала ALRM в Perl


Если мы запускаем из скрипта на Perl какую-то внешнюю программу, может случиться так, что программа эта насмерть зависнет - и следующая после system строчка скрипта никогда не выполнится. Бороться тут можно двумя способами - запуском задания в фоне и обработкой сигнала ALRM. Первый способ не рассматриваем по причине его очевидности (& писать все умеют - fork/exec тоже всем известен), второй немножко интереснее. Простой пример кода:
alarm 60;
system "/very/slow/command";
alarm 0; # это мы сбрасываем будильник, если он не сработал
Если команда, вызванная system, провозится больше минуты, то скрипт аварийно завершится с сообщением "Alarm clock". Если такое поведение нас не устраивает (т.е. зависание дочерней программы не является фатальным), то все несколько сложнее. Последовательность alarm/system/alarm 0 нужно поместить внутрь оператора
eval {}; - и поместить в начало этого блока собственную обработку сигнала ALRM - в самом простом случае такую:
local $SIG{ALRM} = sub {die "Signal ALRM fired\n"};
Этот die не приведет к выходу из программы - только завершит оператор eval {}; - что нам и требовалось. Еще необходимо учесть один момент - описанная схема - прекрасный генератор zombie-процессов. Для борьбы с ними где-то в программе нужно поместить такой цикл:
use POSIX ':sys_wait_h';
while (waitpid(-1, WNOHANG) > 0) {};
Подробнее про работу с сигналами можно почитать в perlipc (1).

Комментариев нет: