По получении сигнала ядро останавливает процесс, где бы он ни был. Затем оно имитирует вызов процедуры обработчика сигнала, передавая ему номер сигнала в качестве ее единственного аргумента. Ядро устраивает все таким образом, что нормальный возврат из функции обработчика сигнала (либо посредством
return
, либо в результате выпадения из конца функции) передает управление в ту точку программы, в которой она находилась в момент появления сигнала.
Что происходит после обработки сигнала, когда тот же самый сигнал появится в следующий раз снова? Остается ли обработчик на том же месте? Или же он сбрасывается, и для сигнала используется действие по умолчанию? Ответ, по историческим
причинам, «зависит от». В частности, стандарт С оставляет это на усмотрение реализации.
На практике V7 и традиционные системы System V, такие, как Solaris, устанавливают для сигнала действие по умолчанию.
Давайте рассмотрим простой обработчик сигнала в действии под Solaris. Следующая программа,
ch10-catchint.c
, перехватывает
SIGINT
. Обычно вы генерируете этот сигнал, набирая на клавиатуре CTRL-C.
1 /* ch10-catchint.c - перехват SIGINT, по крайней мере, однажды. */
2
3 #include <signal.h>
4 #include <string.h>
5 #include <unistd.h>
6
7 /* handler --- простой обработчик сигнала. */
8
9 void handler(int signum)
10 {
11 char buf[200], *cp;
12 int offset;
13
14 /* Пройти через это испытание , чтобы избежать fprintf. */
15 strcpy(buf, "handler: caught signal ");
16 cp = buf + strlen(buf); /* cp указывает на завершающий '\0' */
17 if (signum > 100) /* маловероятно */
18 offset = 3;
19 else if (signum > 10)
20 offset = 2;
21 else
22 offset = 1;
23 cp += offset;
24
25 *cp-- = '\0'; /* завершить строку */
26 while (signum >0) { /* work backwards, filling in digits */
27 *cp-- = (signum % 10) + '0';
28 signum /= 10;
29 }
30 strcat(buf, "\n");
31 (void)write(2, buf, strlen(buf));
32 }
33
34 /* main --- установить обработку сигнала и войти в бесконечный цикл */
35
36 int main(void)
37 {
38 (void)signal(SIGINT, handler);
39
40 for(;;)
41 pause; /* ждать сигнал, см. далее в главе */
42
43 return 0;
44 }
Строки 9–22 определяют функцию обработки сигнала (остроумно названную
Поскольку V7 и другие традиционные системы восстанавливают действие сигнала по умолчанию, поэтому когда вы хотите снова получить сигнал в будущем, функция обработчика должна немедленно переустановить саму себя:
. [107] На системах BSD обработчик сигнала после его возвращения остается на месте. Системы GNU/Linux следуют поведению BSD. Вот что происходит под GNU/Linux:
107
Изменение поведения было плохой мыслью, сильно критиковавшейся в свое время, но было слишком поздно. Изменение семантики определенного интерфейса всегда ведет к проблеме, как было в этом случае. Хотя это особенно относится к проектировщикам операционных систем, любой, кто разрабатывает библиотеки общего назначения, также должен помнить этот урок. — Примеч. автора.
$ ch10-catchint /* Запустить программу */
handler: caught signal 2 /* Набираем ^C, вызывается обработчик */
handler: caught signal 2 /* И снова... */
handler: caught signal 2 /* И снова! */
handler: caught signal 2 /* Помогите! */
handler: caught signal 2 /* Как нам это остановить?! */
Quit (core dumped) /* ^\, генерирует SIGQUIT. Bay */