Void sigint_handler( int signo) ⇐ ПредыдущаяСтр 4 из 4
{ printf("Ctrl-C pressed\n"); if (++cnt == 3) { signal(SIGINT, SIG_DFL); raise(SIGINT); } } Int main( void) { signal(SIGINT, sigint_handler); while (1) { printf("Some string to print\n"); } return 0; } Обратите внимание, как происходит выход из программы в случае, когда три раза подряд нажата комбинация Ctrl-C. Обработчик сигнала SIGINT устанавливается в стандартное значение по умолчанию (то есть выход из программы), затем сигнал SIGINT посылается самому себе с помощью функции raise. Это гарантирует, что родительскому процессу будет сообщена истинная причина завершения программы: получение сигнала SIGINT. Если бы программа завершала свою работу по вызову exit(0), родительский процесс получил бы информацию, что наш процесс завершился нормально с кодом завершения 0. Какой из двух вариантов завершения программы: посылка сигнала самой себе еще раз или выход по exit — предпочтительнее, зависит от конкретной ситуации. Приведённая выше программа имеет серьёзный дефект, связанный с тем, что сигнал может поступить в программу и начать обрабатываться в любой момент времени. Если сигнал поступит, например, в середине работы функции printf, выполнение функции printf будет приостановлено, и начнется выполнение обработчика сигнала, который вновь вызовет printf. Получается, что функция будет вызвана вновь из середины самой себя. Большинство функций стандартной библиотеки не будут корректно работать в этой ситуации. Функция, которая может безопасно вызываться из обработчика сигнала, называется асинхронно-безопасной. Чтобы устранить этот дефект можно поступить двумя способами: во-первых, на время выполнения функции printf можно заблокировать сигнал SIGINT. Блокирование сигналов будет рассмотрено ниже. Во-вторых, можно переписать программу так, чтобы обработчик сигнала просто устанавливал некоторую глобальную переменную, которую будет проверять основная программа. Основная программа будет печатать сообщение о нажатой клавише. #include <stdio.h> #include <signal.h> int cnt = 0; volatile int flag = 0; Void sigint_handler( int signo) { flag = 1; if (++cnt == 3) { signal(SIGINT, SIG_DFL); raise(SIGINT); } } Int main( void) { signal(SIGINT, sigint_handler); while (1) { if (flag) { printf("Ctrl-C pressed\n"); flag = 0; } else { printf ("Some string to print\n" ); } } return 0; } Ключевое слово volatile в определении переменной flag говорит компилятору, что значение переменной может быть изменено асинхронно, и поэтому компилятор не должен пытаться оптимизировать обращения к этой переменной, например, сохраняя её в регистрах процессора. Новый вариант программы будет работать при выводе в файл или на консоль.
Функция sigaction Системный вызов sigaction позволяет установить обработчик сигнала. Функция описана следующим образом: #include <signal.h> int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact); Структура sigaction описана следующим образом: struct sigaction { void (*sa_handler)( int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)( void); }; Функция sigaction устанавливает обработчик для сигнала signum, который задается в аргументе act, если этот аргумент не равен NULL. Если аргумент oldact не равен NUL информация о предыдущем обработчике возвращается в структуру, на которую указывает oldact. Поле sa_restorer сохранено для совместимости со старым программным обеспечением и не должно использоваться. Поле sa_sigaction позволяет задать обработчик сигнала, которому будет передаваться больше информации, чем обычно. Поле sa_handler задаёт обработчик сигнала. Он может быть равен SIG_IGN ил SIG_DFL, что означает игнорирование сигнала или установку обработчика по умолчанию для сигнала. Поле sa_mask задаёт множество сигналов, которые будут заблокированы на время работы обработчика сигнала. Поле sa_flags позволяет определить режим, в котором будет обрабатываться сигнал. Значение этого поля получается объединением значений флагов, перечисленных ниже. SA_NOCLDSTOP - Может задаваться только для сигнала SIGCHLD. В этом случае процесс не будет получать сигнал SIGCHLD, когда какой-либо из его дочерних процессов будет остановлен, то есть получит сигнал SIGSTOP, SIGTSTP, SIGTTIN или SIGTTOU. SA_ONESHOT или SA_RESETHAND - устанавливает обработку сигнала по умолчанию перед тем, как будет вызван обработчик сигнала (семантика System V функции signal). SA_RESTART - Устанавливает перезапуск системных вызовов после возврата из обработчика сигнала (семантика BSD функции signal). SA_NOMASK или SA_NODEFER - Не блокирует получение сигнала в обработчике этого сигнала (семантика System V функции signal). SA_SIGINFO - Если установлен этот флаг, функция обработки сигнала будет получать 3 аргумента вместо одного. В этом случае адрес функции-обработчика сигнала должен содержаться в sa_sigaction, а не в sa_handler. Из всех этих флагов стандарт POSIX определяет только SA_NOCLDSTOP. Остальные флаги являются расширениями стандарта. В системах BSD и Linux поддерживаются все флаги.
Пример программы Следующий пример делает то же самое, что и предыдущий пример, но использует функцию sigaction. #include <stdio.h> #include <signal.h> int cnt = 0; volatile int flag = 0; struct sigaction orig_sigint_handler; void sigint_handler(int signo) { flag = 1; if (++cnt ==3) { sigaction(SIGINT, &orig_sigint_handler); raise(SIGINT); } } Int main( void) { struct sigaction sa; sa.sa_handler = sigint_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART; sigaction(SIGINT, &sa, &orig_sigint_handler); while (1) { if (flag) { printf("Ctrl-С pressed\n"); flag = 0; } else { printf ("Some string to print\n" ); } } return 0; }
©2015 arhivinfo.ru Все права принадлежат авторам размещенных материалов.
|