Программные прерывания, предоставляющие механизм для обработки асинхронных событий. Такие события могут инициироваться пользователем (нажатие клавиш ctrl+c) или возникать вследствие действий в программе или ядре.
Отличительная особенность сигналов состоит в том, что они появляются асинхронно, и обработка их производится тоже асинхронно. Могут быть написаны функции обработки сигналов, эти функции регистрируются процессом в ядре операционной системы, а операционная система инициирует выполнение этих функций при получении соответствующих сигналов в соответствии с установленными правилами обработки.
Сигналы имеют определенный жизненный цикл:
1) состояние, связанное с генерированием (отправлением, поднятием) сигнала:
generate //
send // смысл: сигнал появился
raise //
2) состояние хранения (store) – ядро операционной системы хранит его до тех пор, пока не появляется условие для доставки сигнала. И после того, как соответствующие условия создаются, ядро инициирует (обрабатывает) поступивший сигнал.
|
|
Ядро может выполнить 3 действия связанные с обработкой сигнала:
1. Игнорирование сигнала. //сигнал направлен процессу, но действий нету. НО есть ряд сигналов, которые нельзя игнорировать:
SIGKILL //связаны с остановкой или «убийством» процесса
SIGSTOP
2. Выполнить действие по умолчанию.
Для всех сигналов предусмотрены действия, которые выполняются по умолчанию. Это или завершение процесса, или может быть действие игнорировать сигнал, захватить и обработать сигнал. Процесс прерывается, и запускается функция обработчик
3. Установление собственного обработчика
Написать функцию обработчик и зарегистрировать её. Но есть некоторые сигналы, которые нельзя описать самостоятельно. Ядро приостанавливает текущее выполнение процесса, и переходит к выполнению ранее зарегистрированной функции. После завершения выполнения функции ядро продолжает обрабатывать остановленный исходный процесс.
Все сигналы имеют символическое имя. Все символические имена описаны в <signal.h> и начинаются с префикса SIG. Сигналы имеют номера (целочисленные). Общее число сигналов системы порядка 30.
SIGKILL | завершить мгновенно, отправляется системным вызовом kill() | завершить процесс |
SIGSTOP | остановить сигнал | остановить процесс |
SIGALARM | отправляется процессу при завершении времени установленного функцией alarm(). | завершить процесс |
SIGHUP | ядро оправляет лидеру сеанса, когда терминал сеанса отключается. Также этот сигнал отправляется всем процессам приоритетной группы, когда лидер сеанса завершается (пользователь выходит из системы) | завершить процесс |
SIGINT | отправляется всем процессам приоритетной группы, при нажатии CTRL+C. Можно написать обработчик. | завершить процесс |
SIGUSR1 | Используются только пользователями. могут использоваться для взаимодействия с процессом демоном. Можно написать обработчик | завершить процесс |
SIGUSR2 |
Методика асинхронного механизма получения и обработки сигнала: обработка сигнала производится во время приостановки самого процесса.
|
|
Системный вызов установки обработчика на сигнал
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signo, sighandler_t handler)
Позволяет зарегистрировать обработчик для сигнала, указанного в качестве первого аргумента. Второй аргумент - адрес функции обработчика. В качестве второго аргумента могут использоваться также:
SIG_DFL //восстановить поведение по умолчанию для сигнала signo.
SIG_IGN //игнорировать сигнал, указанный параметром signo
Функция signal() возвращает предыдущее поведение сигнала, которое может принимать вид указателя на обработчик сигнала, SIG_DFL или SIG_IGN. При ошибке возвращается значение SIG_ERP. Переменная errno не устанавливается.
Функция ожидания:
#include <unistd.h>
int pause (void);
выполнение процесса приостанавливается, пока не будет получен сигнал, допускающий либо обработку, либо завершение процесса. Функция возвращает значение только тогда, когда получен сигнал, который перехватывается, и возвращается -1, а переменная errno = EINTR.
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
static void sig_hand(int signo)
{signal (signo, sig_hand);
if (signo == SIGINT) printf(“получен сигнал SIGINT ”);
else if (signo == SIGTERM) printf(“ получен сигнал SIGTERM”)
else {printf(“неописанный сигнал”); exit(EXIT_FAILURE);}
exit(EXIT_SUCCESS);}
int main()
{
if (signal (SIGINT, sig_hand) == SIG_ERR) {printf(“ошибка SIGINT”);
exit(-1);}
if (signal(SIGTERM, sig_hand) == SIR_ERR) {printf(“ошибка SIGTERM”);
exit(-2);}
if (signal(SIGHUP, SIG_IGN) == SIR_ERR) {printf(“невозможно игнорировать SIGHUP”);
exit(-3);}
for (;;) pause();
}
Когда порождается новый процесс, для него устанавливается поведение всех сигналов по умолчанию, если только родительский процесс не игнорирует какие-то сигналы. Таким образом, новый процесс выполняет действие по умолчанию для всех сигналов захватываемых предком, а все прочие сигналы не вызывают действий
#include <signal.h> <stdlib.h> <stdio.h> <unistd.h>
if (signal(SIGINT, SIG_IGN)!= SIGIGN)
{if (signal(SIGINT, sigint_handler) == SIG_ERR) {…}}
Свойство игнорирования сигналов порожденным процессом, заимствованное у родительского процесса, можно использовать для создания фоновых процессов. Для этого перед тем как фоновый процесс запускается для сигналов SIGINT & SIGQUIT, устанавливается игнорирование этих сигналов. Поэтому процессы, которые производят перехват этих сигналов перед установкой обработчика проверяют, не было ли установлено игнорирование для этих сигналов.