Здавалка
Главная | Обратная связь

Void sigint_handler( int signo)



{

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 Все права принадлежат авторам размещенных материалов.