Книга: Linux программирование в примерах

10.4.1. Традиционные системы

10.4.1. Традиционные системы

После помещения на место обработчика сигнала ваша программа развивается своим путем. Интересные вещи возникают лишь с появлением сигнала (например, пользователь нажал CTRL-C для прерывания вашей программы, или был сделан вызов raise()).

По получении сигнала ядро останавливает процесс, где бы он ни был. Затем оно имитирует вызов процедуры обработчика сигнала, передавая ему номер сигнала в качестве ее единственного аргумента. Ядро устраивает все таким образом, что нормальный возврат из функции обработчика сигнала (либо посредством 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 указывает на завершающий '' */
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-- = ''; /* завершить строку */
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 определяют функцию обработки сигнала (остроумно названную handler()[106]). Все, что эта функция делает, — выводит номер перехваченного сигнала и возвращается. Для вывода этого сообщения она выполняет множество ручной работы, поскольку fprintf() не является «безопасной» для вызова из обработчика сигнала. (Вскоре это будет описано в разделе 10.4.6 «Дополнительные предостережения».)

Функция main() устанавливает обработчик сигнала (строка 38), а затем входит в бесконечный цикл (строки 40–41). Вот что происходит при запуске:

$ ssh solaris.example.com
 /* Зарегистрироваться на доступной системе Solaris */
Last login: Fri Sep 19 04:33:25 2003 from 4.3.2.1.
Sun Microsystems Inc. SunOS 5.9 Generic May 2002
$ gcc ch10-catchint.c /* Откомпилировать программу */
$ a.out /* Запустить ее */
^C handler: caught signal 2 /* Набрать ^C, вызывается обработчик */
^C /* Попробовать снова, но на этот раз... */
$ /* Программа завершается */

Поскольку V7 и другие традиционные системы восстанавливают действие сигнала по умолчанию, поэтому когда вы хотите снова получить сигнал в будущем, функция обработчика должна немедленно переустановить саму себя:

void handler(int signum) {
 char buf[200], *cp;
 int offset;
 (void)signal(signum, handler); /* переустановить обработчик */
 /* ...оставшаяся часть функции как прежде... */
}

Оглавление книги


Генерация: 2.019. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз