Сигнал - программное прерывание , посылаемое процессу .
Операционка использует сигналы для получения отчета в случае возникновения аварийных ситуаций .
Это может быть указатель на неправильный адрес в памяти .
Следующие события могут вызывать сигналы :
1. Программные ошибки
2. Нажатие клавиш Ctrl-C
3. Срабатывание таймера
4. Вызов kill , raise
События , которые генерят сигналы , можно разбить на 3 категории :
1. Ошибки
2. Внешние события
3. Явный запрос
Не все программные ошибки генерят сигналы - например , открытие несуществующего сигнала
не генерит сигнала . Внешние события связаны с I/O или многозадачностью .
Явный запрос - это kill .
Сигналы могут быть synchronously или asynchronously. Первые блокируют выполнение программы
и обычно это программные ошибки .
Каждый сигнал имеет свое адекватное действие , которое задокументировано . Для большинства
сигналов это прекращение процесса . При этом сигнал пишет свой core dump file .
Каждый сигнал определен как макрос , имеющий свое целочисленное значение .
Группа программных сигналов :
int SIGFPE - арифметическая ошибка
int SIGILL - invalid object , выход за границы массива и т.д.
int SIGSEGV - выход за пределы доступной памяти
int SIGBUS - invalid pointer
int SIGSYS - bad system call
Группа Termination Signals
int SIGTERM - в отличие от SIGKILL , его можно проигнорировать
int SIGINT - Ctrl-C
int SIGQUIT - аналогичен SIGINT
int SIGKILL-немедленное прекращение процесса
int SIGHUP - сетевой разрыв
Группа Alarm Signals
int SIGALRM - вызов функции alarm
int SIGVTALRM - CPU timer
Группа Asynchronous I/O Signals
int SIGIO - файловый дескриптор готов к выполнению операций ввода-вывода
int SIGURG - при появлении out-of-band данных в сокете
Группа Job Control Signals
int SIGCHLD - посылается родительскому процессу дочерним при остановке
int SIGCONT - сигнал на продолжение процесса
int SIGSTOP - останавливает процесс
Для распечатки соответствующего сообщения нужно использовать функции strsignal и psignal.
Следующий пример показывает , как удалить временный файл при возникновении сигнала :
#include < signal.h >
void termination_handler (int signum)
{
struct temp_file *p;
for (p = temp_file_list; p; p = p->next)
unlink (p->name);
}
intmain (void)
{
...
if (signal (SIGINT, termination_handler) == SIG_IGN) signal (SIGINT, SIG_IGN);
if (signal (SIGHUP, termination_handler) == SIG_IGN) signal (SIGHUP, SIG_IGN);
if (signal (SIGTERM, termination_handler) == SIG_IGN)signal (SIGTERM, SIG_IGN);
...
}
Следующий пример показывает , как обработать программное прерывание в цикле :
#include < signal.h >
#include < stdio.h >
#include < stdlib.h >
/* This flag controls termination of the main loop. */
volatile sig_atomic_t keep_going = 1;
/* The signal handler just clears the flag and re-enables itself. */
void catch_alarm (int sig)
{
keep_going = 0;
signal (sig, catch_alarm);
}
void do_stuff (void)
{
puts ("Doing stuff while waiting for alarm....");
}
int main (void)
{
/* Establish a handler for SIGALRM signals. */
signal (SIGALRM, catch_alarm);
/* Set an alarm to go off in a little while. */
alarm (2);
/* Check the flag once in a while to see when to quit. */
while (keep_going)
do_stuff ();
return EXIT_SUCCESS;
}
В следующем примере родительский процесс порождает дочерний , который посылает родителю
сигнал с использованием kill :
#include < signal.h >
#include < stdio.h >
#include < sys/types.h >
#include < unistd.h >
/* When a SIGUSR1 signal arrives, set this variable. */
volatile sig_atomic_t usr_interrupt = 0;
void synch_signal (int sig)
{
usr_interrupt = 1;
}
/* The child process executes this function. */
void child_function (void)
{
/* Perform initialization. */
printf ("I'm here!!! My pid is %d.\n", (int) getpid ());
/* Let parent know you're done. */
kill (getppid (), SIGUSR1);
/* Continue with execution. */
puts ("Bye, now....");
exit (0);
}
int main (void)
{
struct sigaction usr_action;
sigset_t block_mask;
pid_t child_id;
/* Establish the signal handler. */
sigfillset (&block_mask);
usr_action.sa_handler = synch_signal;
usr_action.sa_mask = block_mask;
usr_action.sa_flags = 0;
sigaction (SIGUSR1, &usr_action, NULL);
/* Create the child process. */
child_id = fork ();
if (child_id == 0)
child_function (); /* Does not return. */
/* Busy wait for the child to send a signal. */
while (!usr_interrupt) ;
/* Now continue execution. */
puts ("That's all, folks!");
return 0;
}
В следующем примере показана обработка сигналов в критической части кода с использованием sigprocmask:
/* This variable is set by the SIGALRM signal handler. */
volatile sig_atomic_t flag = 0;
int main (void)
{
sigset_t block_alarm;
...
/* Initialize the signal mask. */
sigemptyset (&block_alarm);
sigaddset (&block_alarm, SIGALRM);
while (1)
{
/* Check if a signal has arrived; if so, reset the flag. */
sigprocmask (SIG_BLOCK, &block_alarm, NULL);
if (flag)
{
actions-if-not-arrived
flag = 0;
}
sigprocmask (SIG_UNBLOCK, &block_alarm, NULL);
...
}
}
Надежный способ блокировки сигналов - использование sa_mask в структуре sigaction :
#include < signal.h >
#include < stddef.h >
void catch_stop ();
void install_handler (void)
{
struct sigaction setup_action;
sigset_t block_mask;
sigemptyset (&block_mask);
/* Block other terminal-generated signals while handler runs. */
sigaddset (&block_mask, SIGINT);
sigaddset (&block_mask, SIGQUIT);
setup_action.sa_handler = catch_stop;
setup_action.sa_mask = block_mask;
setup_action.sa_flags = 0;
sigaction (SIGTSTP, &setup_action, NULL);
}
|
|