Пример. Программа “Будильник”

Пример 2.

Пример 1.

В данном примере при получении сигнала 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;

}

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

#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 */

exit(0);

}

int main(int argc, char **argv)

{

signal (SIGINT, SigHndlr); /*установка реакции на сигнал */

creat(tempfile, 0666); /*создание временного файла*/

unlink(tempfile);

/*уничтожение временного файла в случае нормального функционирования процесса */

return 0;

}

Существуют задачи, в которых необходимо прервать выполнение процесса по истечении некоторого количества времени. Средствами ОС “заводится” будильник, который будет поторапливать ввести некоторое имя. Системный вызов 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 (;;)

{

printf(“имя:”);

if (gets(s)!= NULL) break; /* ожидаем ввода имени */

};

printf(“OK! \n”);

return 0;

}

В начале программы мы устанавливаем реакцию на сигнал SIGALRM - функцию alarm(), далее мы заводим будильник, запрашиваем “ Введите имя ” и ожидаем ввода строки символов. Если ввод строки задерживается, то будет вызвана функция alarm(), которая напомнит, что программа “ждет имя”, опять заведет будильник и поставит себя на обработку сигнала SIGALRM еще раз. И так будет до тех пор, пока не будет введена строка. Здесь имеется один нюанс: если в момент выполнения системного вызова возникает событие, связанное с сигналом, то система прерывает выполнение системного вызова и возвращает код ответа, равный «-1».

Пример. Двухпроцессный вариант программы “Будильник”.

#include <signal.h>

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

void alr(int s)

{

printf(“\n Быстрее!!! \n”);

signal(SIGALRM, alr);

/* переустановка обработчика alr на приход сигнала SIGALRM */

}

int main(int argc, char **argv)

{

char s[80];

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. Все, что ниже цикла, будет выполняться в процессе-сыне: мы ожидаем ввода строки, если ввод осуществлен, то происходит уничтожение отца (SIGKILL).

Неименованные каналы.

Одним из простейших средств взаимодействия процессов в операционной системе UNIX является механизм каналов. Неименованный канал- область на диске, к которой не возможен доступ по имени, а только с помощью двух дискрипторов с ней ассоциированных. Один для чтения, другой – для записи.

Отличитльные свойства:

1. В отличии от файла, который тоже является областью на диске и доступ к нему может быть также осуществлен по дескриптору, канал не имеет имени, т.е. доступ к нему возможен только по этим двум дескрипторам, т.е. его невозможно, как правило, открыть, используя какое-то имя. Его можно только создать, и в ответ будет получено два дескриптора, и ими уже можно пользоваться.

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

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

4. Главное отличие от файла – это то, что в нем реализуется строго последовательный доступ к данным. Это означает, что данные могут быть получены только в том порядке, в котором они были в канал положены. Если в файле был возможен произвольный доступ к данным, то в канале это невозможно.

Одним из простейших средств взаимодействия процессов в операционной системе UNIX является механизм каналов. Неименованный канал есть некая сущность, в которую можно помещать и извлекать данные, для чего служат два файловых дескриптора, ассоциированных с каналом: один для записи в канал, другой — для чтения. Для создания канала служит системный вызов pipe():

int pipe (int *fd)

Данный системный вызов выделяет в оперативной памяти некоторое ограниченное пространство и возвращает че6рез параметр fd массив из двух файловых дескрипторов: один для записи в канал — fd[1], другой для чтения — fd[0].

Эти дескрипторы являются дескрипторами открытых файлов, с которыми можно работать, используя такие системные вызовы как read(), write(), dup() и пр.

Однако существуют различия в организации использования обычного файла и канала.

Особенности организации чтения данных из канала:

 если прочитано меньше байтов, чем находится в канале, оставшиеся сохраняются в канале;

 если делается попытка прочитать больше данных, чем имеется в канале, и при этом существуют открытые дескрипторы записи, ассоциированные с каналом, будет прочитано (т.е. изъято из канала) доступное количество данных, после чего читающий процесс блокируется до тех пор, пока в канале не появится достаточное количество данных для завершения операции чтения;

 процесс может избежать такого блокирования, изменив для канала режим блокировки с использованием системного вызова fcntl(), в этом случае будет считано доступное количество данных, и управление будет сразу возвращено процессу;

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

Особенности организации записи данных в канал:

 если процесс пытается записать большее число байтов, чем помещается в канал (но не превышающее предельный размер канала) записывается возможное количество данных, после чего процесс, осуществляющий запись, блокируется до тех пор, пока в канале не появится достаточное количество места для завершения операции записи;

 процесс может избежать такого блокирования, изменив для канала режим блокировки с использованием системного вызова fcntl(). В неблокирующем режиме в ситуации, описанной выше, будет записано возможное количество данных, и управление будет сразу возвращено процессу.

 если же процесс пытается записать в канал порцию данных, превышающую предельный размер канала, то будет записано доступное количество данных, после чего процесс заблокируется до появления в канале свободного места любого размера (пусть даже и всего 1 байт), затем процесс разблокируется, вновь производит запись на доступное место в канале, и если данные для записи еще не исчерпаны, вновь блокируется до появления свободного места и т.д., пока не будут записаны все данные, после чего происходит возврат из вызова write()

 если процесс пытается осуществить запись в канал, с которым не ассоциирован ни один дескриптор чтения, то он получает сигнал SIGPIPE (тем самым ОС уведомляет его о недопустимости такой операции).

В стандартной ситуации (при отсутствии переполнения) система гарантирует атомарность операции записи, т. е. при одновременной записи нескольких процессов в канал их данные не перемешиваются.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: