Чтение онлайн

ЖАНРЫ

Linux программирование в примерах
Шрифт:

Помимо внешнего генерирования, сигнал может быть отправлен непосредственно самой программой с использованием стандартной функции С

raise
:

#include <signal.h> /* ISO С */

int raise(int sig);

Эта функция посылает сигнал

sig
вызывающему процессу. (Это действие имеет свое применение; вскоре мы увидим пример.)

Поскольку

raise
определена стандартом С, для процесса это наиболее переносимый способ отправить себе сигнал. Есть другие способы, которые мы обсудим далее в главе.

10.4. Обработчики сигналов в действии

Множество

осложнений и отклонений обнаруживается после установки на место обработчика, после его вызова и впоследствии возвращения.

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 указывает на завершающий '\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 определяют функцию обработки сигнала (остроумно названную

handler
[106] ). Все, что эта функция делает, — выводит номер перехваченного сигнала и возвращается. Для вывода этого сообщения она выполняет множество ручной работы, поскольку
fprintf
не является «безопасной» для вызова из обработчика сигнала. (Вскоре это будет описано в разделе 10.4.6 «Дополнительные предостережения».)

106

Handler (англ.) — обработчик — Примеч. перев.

Функция

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); /* переустановить обработчик */

 /* ...оставшаяся часть функции как прежде... */

}

10.4.2. BSD и GNU/Linux

BSD 4.2 изменила способ работы

signal
. [107] На системах BSD обработчик сигнала после его возвращения остается на месте. Системы GNU/Linux следуют поведению BSD. Вот что происходит под GNU/Linux:

107

Изменение поведения было плохой мыслью, сильно критиковавшейся в свое время, но было слишком поздно. Изменение семантики определенного интерфейса всегда ведет к проблеме, как было в этом случае. Хотя это особенно относится к проектировщикам операционных систем, любой, кто разрабатывает библиотеки общего назначения, также должен помнить этот урок. — Примеч. автора.

Поделиться с друзьями: