Н.В. Вдовикина, А.В. Казунин, И.В. Машечкин, А.Н. Техехин - Системное программное обеспечение - взаимодействие процессов (1114927), страница 12
Текст из файла (страница 12)
Обработка такойситуации в разных версиях UNIX реализована по-разному,например, обработка сигнала может быть отложена до завершениясистемного вызова; либо системный вызов автоматическиперезапускается после его прерывания сигналом; либо системныйвызов вернет –1, а в переменной errno будет установлено значениеEINTRДля отправки сигнала существует системный вызов kill():#include <sys/types.h>#include <signal.h>int kill (pit_t pid, int sig)Первым параметром вызова служит идентификатор процесса,которому посылается сигнал (в частности, процесс может послатьсигнал самому себе). Существует также возможность одновременнопослать сигнал нескольким процессам, например, если значение66этого параметра есть 0, сигнал будет передан всем процессам,которые принадлежат той же группе, что и процесс, посылающийсигнал, за исключением процессов с идентификаторами 0 и 1.Во втором параметре передается номер посылаемого сигнала.Если этот параметр равен 0, то будет выполнена проверкакорректности обращения к kill() (в частности, существованиепроцесса с идентификатором pid), но никакой сигнал вдействительности посылаться не будет.Еслипроцесс-отправительнеобладаетправамипривилегированного пользователя, то он может отправить сигналтолько тем процессам, у которых реальный или эффективныйидентификатор владельца процесса совпадает с реальным илиэффективным идентификатором владельца процесса-отправителя.Для определения реакции на получение того или иногосигнала в процессе служит системный вызов signal():#include <signal.h>void (*signal( int sig, void (*disp) (int))) (int)где аргумент sig — номер сигнала, для которогоустанавливается реакция, а disp — либо определеннаяпользователем функция-обработчик сигнала, либо одна из констант:SIG_DFL и SIG_IGN.
Первая из них указывает, что необходимоустановить для данного сигнала обработку по умолчанию, т.е.стандартную реакцию системы, а вторая — что данный сигналнеобходимо игнорировать. При успешном завершении функциявозвращает указатель на предыдущий обработчик данного сигнала(он может использоваться процессом, например, для восстановленияпрежней реакции на сигнал).Как видно из прототипа вызова signal(), определеннаяпользователем функция-обработчик сигнала должна принимать одинцелочисленный аргумент (в нем будет передан номеробрабатываемого сигнала), и не возвращать никаких значений.Отметим одну особенность реализации сигналов в раннихверсиях UNIX: каждый раз при получении сигнала его диспозиция(т.е.
действие при получении сигнала) сбрасывается на действие поумолчанию, т.о. если процесс желает многократно обрабатыватьсигнал своим собственным обработчиком, он должен каждый разпри обработке сигнала заново устанавливать реакцию на него (см.пример 9)В заключении отметим, что механизм сигналов являетсядостаточно ресурсоемким, ибо отправка сигнала представляет собой67системный вызов, а доставка сигнала - прерывание выполненияпроцесса-получателя.
Вызов функции-обработчикаи возвраттребует операций со стеком. Сигналы также несут весьмаограниченную информацию.Пример 10. Обработка сигнала.В данном примере при получении сигнала SIGINT четыреждывызывается специальный обработчик, а в пятый раз происходитобработка по умолчанию.#include <sys/types.h>#include <signal.h>#include <stdio.h>int count = 0;void SigHndlr (int s)/* обработчик сигнала */{printf("\n I got SIGINT %d time(s) \n",++ count);if (count == 5) signal (SIGINT, SIG_DFL);/* ставим обработчик сигнала по умолчанию */else signal (SIGINT, SigHndlr);/* восстанавливаем обработчик сигнала */}int main(int argc, char **argv){signal (SIGINT, SigHndlr); /* установка реакциина сигнал */while (1); /*”тело программы” */return 0;}Пример 11.
Удаление временных файлов при завершениипрограммы.При разработке программ нередко приходится создаватьвременные файлы , которые позже удаляются. Если произошлонепредвиденное событие, такие файлы могут остаться неудаленными. Ниже приведено решение этой задачи.68#include <unistd.h>#include <signal.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>const char * tempfile = “abc”;void SigHndlr (int s){unlink(tempfile);/* уничтожение временного файла в случаеприхода сигнала SIGINT.
В случае, если такой файл несуществует (еще не создан или уже удален), вызоввернет -1 */}int main(int argc, char **argv){signal (SIGINT, SigHndlr); /*установка реакциина сигнал */…creat(tempfile, 0666); /*создание временногофайла*/…unlink(tempfile);/*уничтожение временного файла в случаенормального функционирования процесса */return 0;}В данном примере для созданияиспользуется системный вызов creat():временного#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int creat(const char *pathname, mode_t mode);69файлаА системный вызов unlink() удаляет имя и файл, на которыйоно ссылается.#include <unistd.h>int unlink(const char *pathname);Пример 12. Программа “Будильник”.Программа “Будильник”.
Существуют задачи, в которыхнеобходимо прервать выполнение процесса по истечениинекоторого количества времени. Средствами ОС“заводится”будильник, который будет поторапливать ввести некоторое имя.Системный вызов alarm():#include <unistd.h>unsigned int alarm(unsigned int seconds);инициализирует отложенное появление сигнала SIGALRM - процессзапрашивает ядро отправить ему самому сигнал по прошествииопределенного времени.#include <unistd.h>#include <signal.h>#include <stdio.h>void alrm(int s) /*обработчик сигнала SIG_ALRM */{printf(“\n жду имя \n”);alarm(5); /* заводим будильник */signal(SIGALRM,alrm);реакцию на сигнал *//*переустанавливаем}int main(int argc, char **argv){char s[80];signal(SIGALRM, alrm);/* установка обработчика alrm на приход сигналаSIG_ALRM */alarm(5); /* заводим будильник */printf(“Введите имя \n”);for (;;)70{printf(“имя:”);if (gets(s) !=ввода имени */NULL)break;/*ожидаем};printf(“OK! \n”);return 0;}В начале программы мы устанавливаем реакцию на сигналSIGALRM - функцию alarm(), далее мы заводим будильник,запрашиваем “Введите имя” и ожидаем ввода строки символов.Если ввод строки задерживается, то будет вызвана функцияalarm(), которая напомнит, что программа “ждет имя”, опятьзаведет будильник и поставит себя на обработку сигнала SIGALRMеще раз.
И так будет до тех пор, пока не будет введена строка. Здесьимеется один нюанс: если в момент выполнения системного вызовавозникает событие, связанное с сигналом, то система прерываетвыполнение системного вызова и возвращает код ответа, равный «1».Пример 13. Двухпроцессный“Будильник”.вариантпрограммы#include <signal.h>#include <sys/types.h>#include <unistd.h>#include <stdio.h>void alr(int s){printf(“\n Быстрее!!! \n”);signal(SIGALRM, alr);/* переустановкасигнала SIGALRM */обработчика}int main(int argc, char **argv){char s[80];71alrнаприходint pid;signal(SIGALRM, alr);/* установка обработчика alr на приход сигналаSIGALRM */if(pid = fork()) {for (;;){sleep(5);/*приостанавливаемпроцесс на 5 секунд */kill(pid, SIGALRM);/*отправляем сигнал SIGALRM процессусыну */}}else {printf(“Введите имя \n”);for (;;){printf(“имя:”);if (gets(s) != NULL) break; /*ожидаемввода имени*/}printf(“OK!\n”);kill(getppid(), SIGKILL);/* убиваем зациклившегося отца */}return 0;}В данном случае программа реализуется в двух процессах.
Каки в предыдущем примере, имеется функция реакции на сигналalr(), которая выводит на экран сообщение и переустанавливаетфункцию реакции на сигнал, опять же на себя. В основнойпрограмме мы также указываем alr() как реакцию на SIGALRM.После этого мы запускаем сыновний процесс, и отцовский процесс(бесконечный цикл) “засыпает” на 5 единиц времени, после чегосыновнему процессу будет отправлен сигнал SIGALRM. Все, что нижецикла, будет выполняться в процессе-сыне: мы ожидаем ввода72строки, если ввод осуществлен, то происходит уничтожение отца(SIGKILL).5.2 Надежные сигналы.Вышеописанная реализация механизма сигналов имела местов ранних версиях UNIX (UNIX System V.2 и раньше).