Переустановка реакции на сигнал - пример



Сигналы

Введение

Сигналы — это сервис, предоставляемый ядром системы и используемый, главным образом, для обработки ошибок и исключительных ситуаций. Сигналы могут быть посланы ядром или процессом процессу или группе процессов. Например, сигнал посылается когда нажимается клавиша Ctrl-C, чтобы убить исполняющуюся программу. Также, сигнал посылается, когда программа пытается поделить на ноль. В обоих названных случаях, сигналы посылаются ядром.

Сигналы можно рассматривать как программный аналог аппаратных прерываний. Они могут быть обработаны получающим процессом.

 

Сигналы

Сигналы — это механизм передачи сообщений между процессами и от ядра к процессам. Они оповещают процесс о том, что произошло определённое событие. Говорят, что сигнал генерируется для процесса (или посылается процессу), когда в первый раз происходит событие, связанное с этим сигналом. Тип сигнала показывает, какое событие произошло.

Сигналы могут генерироваться программно, с использованием системных вызовов kill(2) и sigsend(2). Хотя говорят, что сигналы посылаются от процесса к процессу, на самом деле они посылаются через ядро. Ещё один программный способ генерации сигналов — это системный вызов alarm(2), который позволяет установить будильник (alarm clock). Будильник в Unix — это сигнал, генерируемый ядром в заданный момент времени.

Кроме того, различные аппаратные события могут генерировать сигналы. Например, попытка записать значение по недопустимому виртуальному адресу или деление на ноль вынуждают ядро генерировать сигнал. Внешние события, такие, как разрыв линии связи или нажатие клавиши INTR (Ctrl-C по умолчанию), также могут быть причиной возникновения сигналов.

Процесс может определить действия, которые должны быть выполнены при получении сигнала. Такие действия называются реакцией на сигнал. Каждый сигнал имеет реакцию по умолчанию, которая выполняется, если для этого сигнала в этом процессе не были явно определены какие-то другие действия. Для большинства сигналов реакция по умолчанию — завершение процесса. Процесс имеет возможности либо проигнорировать сигнал, либо исполнить функцию обработки при его получении. Когда для определённого сигнала установлена реакция, она не меняется, пока не будет явным образом установлена другая реакция.

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

В традиционном Unix, процесс может распознать только один необработанный сигнал данного типа. Если второй сигнал того же типа возникнет прежде, чем первый сигнал будет обработан, второй сигнал будет потерян. Современные Unix-системы поддерживают надёжные сигналы, которые доставляются процессу столько же раз, сколько раз возникло соответствующее событие. Такие сигналы отличаются как по процедуре установки обработчика, так и по процедуре генерации (необходимо использовать функцию sigqueue(3C)). В этом курсе такие сигналы не изучаются.

Предупреждение: Будьте осторожны, используя сигналы для синхронизации процессов. Сигнал проявляет своё присутствие для процесса в дискретные моменты времени, в действительности — в моменты передачи управления от ядра к процессу, то есть в момент выхода из системного вызова или при возобновлении процесса планировщиком. Может оказаться, что процесс получит сигнал до или после того момента, когда он был бы готов обработать его.

 

Типы сигналов

Некоторые сигналы, их действия по умолчанию и события, которые их вызывают, перечислены на следующей странице. Все сигналы перечислены в signal(5); для всех сигналов определены символьные константы в <signal.h>. В соответствии со стандартом POSIX, рекомендуется использовать символические имена, а не численные значения номеров сигналов.

Сигналы от SIGHUP до SIGTERM генерируются ядром, когда возникает соответствующее событие. Сигналы SIGUSR1 и SIGUSR2 могут использоваться программистами для своих целей.

Пример посылки сигнала от ядра к вашей программе — использование клавиши INTR (Ctrl-C по умолчанию), при нажатии которой на терминале генерируется сигнал SIGINT. Ядро, обнаружив незаконное обращение к памяти в вашей программе, генерирует сигнал "нарушение сегментации" (segmentation violation) — SIGSEGV. Аналогично, деление на ноль с плавающей точкой генерирует сигнал "особая ситуации при работе с плавающей точкой (floating point exeption) SIGFPE. Этот же сигнал генерируется и при целочисленном делении на ноль.

Некоторые сигналы зависят от аппаратуры, такие, как SIGEMT и SIGBUS. Поэтому причины и значение этих сигналов могут меняться.

Не путайте аппаратные прерывания и исключения с сигналами. Некоторые аппаратные исключения обрабатываются ядром и иногда переводятся в сигналы, которые посылаются вашей программе.

сигнал реакция событие
SIGHUP exit обрыв линии (см. termio(7))
SIGINT exit прерывание (см. termio(7))
SIGQUIT core завершение (см. termio(7))
SIGILL core неправильная инструкция
SIGTRAP core прерывание трассировки
SIGABORT core аборт
SIGEMT core команда EMT (программное прерывание)
SIGFPE core арифметическая особая ситуация
SIGKILL exit принудительное завершение ("убийство")
SIGBUS core ошибка шины
SIGSEGV core нарушение сегментации
SIGPIPE exit разрыв конвейера
SIGALRM exit будильник
SIGTERM exit программный сигнал прерывания от kill
SIGCLD ignore изменение состояния подпроцесса
SIGPWR ignore сбой питания
SIGSTOP stop остановка (сигналом)
SIGTSTP stop остановка (пользователем) (см. termio(7))
SIGCONT ignore продолжение
SIGTTIN stop ожидание ввода с терминала(см. termio(7))
SIGTTOU stop ожидание вывода на терминал (см.termio(7))
SIGVTALRM exit сигнал виртуального таймера
SIGPROF exit сигнал таймера профилирования
SIGXCPU core исчерпался лимит времени (см. getrlimit(2))
SIGXFSZ core выход за пределы длины файла (см. getrlimit(2))

 

Получение сигнала

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

Доставка сигналов прерывает некоторые системные вызовы, такие как wait(2) или read(2) с медленного символьного устройства. Если сигнал не привёл к завершению процесса, прерванные вызовы возвращают индикацию неуспеха (например, -1) и устанавливают код ошибки равным EINTR. Если вызов был прерван, возможно, вам следует вызвать его снова. SVR4.0 предоставляет возможность автоматического перезапуска системных вызовов, прерванных из-за перехвата сигнала. Это будет обсуждаться, когда пойдёт речь о sigaction(2).

Если не указано иное, процесс выполняет при получении сигнала реакцию по умолчанию. Реакция по умолчанию (SIG_DFL) для каждого сигнала указана на предыдущей странице. В Unix-системах бывает четыре типа реакции по умолчанию:

exit получающий процесс завершается. Слово состояния процесса при этом содержит признак, что процесс был завершён по сигналу.

core получающий процесс завершается, как и при реакции exit. Кроме того, в текущей рабочей директории создаётся дамп памяти (core-файл).

stop получающий процесс останавливается.

ignore получающий процесс игнорирует этот сигнал. Это идентично установке реакции в SIG_IGN.

Если реакция на сигнал установлена в SIG_IGN, полученный сигнал игнорируется, как если бы он никогда не возникал. Если реакция на сигнал является адресом функции, при получении сигнала процесс исполнит функцию обработки по этому адресу.

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

Реакции на сигналы SIGKILL и SIGSTOP не могут быть изменены; кроме того, доставка этих сигналов не может быть заблокирована маской. Когда процесс получает сигналы SIGSTOP, SIGTSTP, SIGTTIN или SIGTTOU, независимо от установленной реакции на них, ожидающий сигнал SIGCONT будет отменен. Когда процесс получает сигнал SIGCONT, независимо от установленной реакции на этот сигнал, все ожидающие сигналы SIGSTOP, SIGTSTP, SIGTTIN или SIGTTOU будут отменены. Кроме того, если процесс был остановлен, он продолжит исполнение.

 

Установка реакции на сигнал

 

Системные вызовы signal(2) и sigset(2) устанавливают реакцию на сигнал, т.е. действия, которые процесс предпринимает, когда получает заданный сигнал. Аргументы:

sig задаёт номер сигнала (кроме SIGKILL или SIGSTOP).

disp задаёт реакцию и может принимать одно из значений:

. SIG_DFL - при получении сигнала sig выполняется реакция по умолчанию.

. SIG_IGN - сигнал sig игнорируется.

. адрес функции - При получении сигнала процесс вызывает заданную функцию. Значение sig передается этой функции как аргумент. Это называется перехватом (catching) сигнала.

. SIG_HOLD (только у sigset(2)) — сигнал блокируется, как если бы он был запрещён маской. Поступающие сигналы соответствующего типа не игнорируются, а запоминаются и при смене реакции на сигнал будут получены процессом.

При использовании signal(2), после перехвата сигнала, реакция на этот сигнала сбрасывается к реакции по умолчанию. Если вы хотите снова перехватывать этот сигнал, вы должны опять переустановить реакцию на вашу функцию обработки. Удобное место для этого — сама функция-обработчик. Но при этом всё равно существует окно, в течении которого действует реакция по умолчанию.

При использовании sigset(2), ядро задерживает новые сигналы до момента возврата из функции обработки. После завершения обработчика, реакция на сигнал снова устанавливается ядром в то же значение, которое было установлено вызовом sigset(2). Если в это время был получен ещё один сигнал, опять вызовется функция обработки.

Вы можете задать различные реакции для каждого сигнала. Также можно использовать одну функцию для обработки различных сигналов.

Значение, возвращаемое signal(2) и sigset(2) — предыдущая установка реакции. Вы можете сохранить это значение и затем восстановить реакцию такой, какой она была изначально.

При выполнении exec(2), сигналы, реакция на которые была установлена в указатель на функцию, сбрасываются в SIG_DFL, в то время, как реакции SIG_IGN и SIG_HOLD действуют и далее.

 

Перехват сигнала - пример

 

Программа в этом примере вычисляет простые числа от 1 до MAXNUM. Эта программа требует много времени для выполнения. Вы можете убить эту программу в любой момент нажатием клавиши Ctrl-C. Вместо того, чтобы просто завершиться, программа перехватывает SIGINT и исполняет функцию, которая показывает количество вычисленных к данному моменту простых чисел, закрывает выходной файл и завершается.

12 Приведено объявление функции обработки сигнала, чтобы передать вызову signal(2) параметр нужного типа (указательн на функцию).

15 Вызывается signal(2). Теперь при получении сигнала SIGINT будет вызвана функция sigcatch(). Заметьте, что если сигнал никогда не будет получен, sigcatch() никогда не будет вызвана.

...  Полный листинг программы приведен в конце раздела. Код, который не показан, выполняет собственно вычисление простых чисел.

29-34  Эта функция обработки сигнала будет вызвана, когда будет перехвачен сигнал SIGINT. Функция выводит сообщение, закрывает выходной файл и завершает программу.

Если бы exit(2) не был вызван в функции обработки сигнала, исполнение возобновилось бы с того места, где оно остановилось внутри main(2). Однако, второй полученный SIGINT прервал бы эту программу, потому что реакция на сигнал была бы установлена ядром в SIG_DFL.

 

Файл: primes1.c


                 ПЕРЕХВАТ СИГНАЛА - ПРИМЕР

 

 

1 #include <stdio.h>

2 #include <signal.h>

3 #define OUTPUT "Primes"

4 #define MAXNUM 10000

5

6 int count;

7 FILE *fptr;

8

9 main()

10 {

11    int number, divisor;

12    void sigcatch();

13

14    fptr = fopen(OUTPUT, "w");

15    signal(SIGINT, sigcatch);

...

25    fclose(fptr);

26 }

27

28

29 void sigcatch()

30 {

31    printf("%d primes computed\n", count);

32    fclose(fptr);

33    exit(1);

34 }

 

Переустановка реакции на сигнал - пример

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

14-15  Устанавливается реакция на сигналы SIGINT и SIGQUIT.  Сигнал SIGQUIT генерируется символом FS (клавиши CTRL и \, нажатые одновременно). Заметьте, что реакция на оба сигнала — одна и та же функция.

29-38  Значение сигнала передаётся как аргумент функции sigcatch(). При перехвате ядро сбрасывает реакцию на сигнал к реакции по умолчанию. Поэтому функция обработки начинает с того что требует ядро игнорировать сигналы того типа, который возник. Это предохраняет программу от завершения, если второй сигнал возникнет во время вычисления функции обработки. Затем, в конце функции, реакция на сигнал снова переустанавливается на вызов sigcatch(). Это нужно сделать для того, чтобы перехватить сигнал снова.

Между перехватом сигнала и вызовом signal(2) с SIG_IGN в функции обработки сигнала, существует небольшой интервал времени, в котором вновь прибывший сигнал может завершить программу. Такая аномалия более вероятна на сильно загруженной системе. Вы можете видеть из вывода программы, что каждый раз, когда нажимается Ctrl-C, вызывается функция обработки.

$ primes2

<Ctrl-C>

111 primes computed

<Ctrl-C>

132 primes computed

<Ctrl-C>

147 primes computed

<Ctrl-C>

177 primes computed

<Ctrl-C>

203 primes computed

<CTRL \>

224 primes computed

 

Файл: primes2.c


          ПЕРЕУСТАНОВКА РЕАКЦИИ НА СИГНАЛ - ПРИМЕР

1 #include <stdio.h>

2 #include <signal.h>

3 #define OUTPUT "Primes"

4 #define MAXNUM 10000

5 int count;

6  FILE *fptr;

7

8 main()

9 {

10 int number, divisor;

11 void sigcatch(int);

12

13 fptr = fopen(OUTPUT, "w");

14 signal(SIGINT, sigcatch);

15 signal(SIGQUIT, sigcatch);

...

25 fclose(fptr);

26 }

27

28

29 void sigcatch(int sig)

30 {

31 signal(sig, SIG_IGN);

32 printf("%d primes computed\n",count);

33 if (sig == SIGQUIT) {

34    fclose(fptr);

35    exit(1);

36 }

37 signal(sig, sigcatch);

38 }

 


Дата добавления: 2018-06-27; просмотров: 415; Мы поможем в написании вашей работы!

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






Мы поможем в написании ваших работ!