siginfoHandler(int sig, siginfo_t *si, void *ucontext)
{
/* НЕБЕЗОПАСНО: В этом обработчике используются функции, небезопасные
для асинхронных сигналов (printf()); см. подраздел 21.1.2) */
/* Можно завершить программу с помощью SIGINT или SIGTERM */
if (sig == SIGINT || sig == SIGTERM) {
allDone = 1;
return;
}
sigCnt++;
printf("caught signal %d\n", sig);
printf(" si_signo=%d, si_code=%d (%s), ", si->si_signo, si->si_code,
(si->si_code == SI_USER)? "SI_USER":
(si->si_code == SI_QUEUE)? "SI_QUEUE": "other");
printf("si_value=%d\n", si->si_value.sival_int);
printf(" si_pid=%ld, si_uid=%ld\n",
(long) si->si_pid, (long) si->si_uid);
sleep(handlerSleepTime);
}
int
main(int argc, char *argv[])
{
struct sigaction sa;
int sig;
sigset_t prevMask, blockMask;
if (argc > 1 && strcmp(argv[1], "-help") == 0)
usageErr("%s [block-time [handler-sleep-time]]\n", argv[0]);
printf("%s: PID is %ld\n", argv[0], (long) getpid());
handlerSleepTime = (argc > 2)?
getInt(argv[2], GN_NONNEG, "handler-sleep-time"): 1;
/* Установить обработчик для большинства сигналов. Во время выполнения
обработчика замаскировать все прочие сигналы для предотвращения рекурсивного
прерывания обработчиков (что сделает вывод неудобочитаемым). */
sa.sa_sigaction = siginfoHandler;
sa.sa_flags = SA_SIGINFO;
sigfillset(&sa.sa_mask);
for (sig = 1; sig < NSIG; sig++)
if (sig!= SIGTSTP && sig!= SIGQUIT)
sigaction(sig, &sa, NULL);
/* Опционально блокировать сигналы и переходить в режим сна, что позволит отправить
сигналы перед тем, как они будут разблокированы и обработаны */
if (argc > 1) {
sigfillset(&blockMask);
sigdelset(&blockMask, SIGINT);
sigdelset(&blockMask, SIGTERM);
if (sigprocmask(SIG_SETMASK, &blockMask, &prevMask) == –1) errExit("sigprocmask");
printf("%s: signals blocked — sleeping %s seconds\n",
argv[0], argv[1]);
sleep(getInt(argv[1], GN_GT_0, "block-time"));
printf("%s: sleep complete\n", argv[0]);
if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == –1)
errExit("sigprocmask");
}
while (!allDone) /* Ожидать входящих сигналов */
pause();
printf("Caught %d signals\n", sigCnt);
exit(EXIT_SUCCESS);
}
signals/catch_rtsigs.c
Прежде чем перейти к объяснению того, что делает функция sigsuspend(), мы разберем ситуацию, при которой нам потребуется воспользоваться этой функцией. Рассмотрим следующий сценарий, с которым разработчики иногда сталкиваются при программировании сигналов.
• Мы временно блокируем сигнал, чтобы его обработчик не прерывал выполнение какого-либо важного участка кода.
• Мы разблокируем сигнал и затем приостанавливаем выполнение программы до тех пор, пока сигнал не будет доставлен.
Чтобы выполнить вышеописанный алгоритм, мы можем попробовать воспользоваться кодом, показанным в листинге 22.4.
Листинг 22.4. Некорректное разблокирование и ожидание сигнала
sigset_t prevMask, intMask;
struct sigaction sa;
sigemptyset(&intMask);
sigaddset(&intMask, SIGINT);
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sa.sa_handler = handler;
if (sigaction(SIGINT, &sa, NULL) == –1)
errExit("sigaction");
/* Заблокировать SIGINT перед выполнением важного участка.
(На этом этапе мы предполагаем, что SIGINT еще не блокирован.) */
if (sigprocmask(SIG_BLOCK, &intMask, &prevMask) == –1)
errExit("sigprocmask — SIG_BLOCK");
/* Критический участок: выполнить работу, которая не должна быть
прервана обработчиком SIGINT */
/* Конец критического участка — восстановить старую маску и разблокировать SIGINT */
if (sigprocmask(SIG_SETMASK, &prevMask, NULL) == –1)
errExit("sigprocmask — SIG_SETMASK");
/* ОШИБКА: что, если SIGINT будет доставлен сейчас… */
pause(); /* Ожидание SIGINT */
В коде из листинга 22.4 есть одна проблема. Предположим, что сигнал SIGINT доставляется после выполнения второго вызова функции sigprocmask(), но перед вызовом функции pause(). (На самом деле сигнал мог быть сгенерирован в любой момент времени, когда программа выполняла критический участок кода, но доставлен он будет только после разблокирования). Доставка сигнала SIGINT приведет к активации обработчика, но после возврата управления из обработчика и возобновления выполнения основной программы вызов функции pause() заблокирует программу до того момента, как будет доставлен