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

22.9. Ожидание сигнала с использованием маски: sigsuspend()

Прежде чем перейти к объяснению того, что делает функция 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() заблокирует программу до того момента, как будет доставлен второй экземпляр сигнала SIGINT. А это, в свою очередь, сделает код бессмысленным, так как его предназначение было в том, чтобы разблокировать сигнал SIGINT и ждать доставки его первого экземпляра.

Перейти на страницу:

Похожие книги