2011. Машбук (1114722), страница 40
Текст из файла (страница 40)
Работа с сигнальной маской.В данном примере анализируется сигнальная маска процесса, и выдаетсясообщение о том, заблокирован ли сигнал SIGINT, и ожидает ли такой сигнал доставки впроцесс. Для того, чтобы легче было увидеть в действии результаты данных операций,предусмотрена возможность добавить этот сигнал к сигнальной маске процесса и послатьэтот сигнал самому себе.#include <signal.h>143#include <sys/types.h>#include <stdlib.h>#include <stdio.h>int main(int argc, char **argv){sigset_t sigset;int fl;sigemptyset(&sigset);printf("Добавить SIGINT к текущей маске? (yes - 1, no- 0) \n");scanf("%d", &fl);if (fl){sigaddset(&sigset, SIGINT);sigprocmask(SIG_BLOCK, &sigset, NULL);}printf("Послать SIGINT? (yes - 1, no - 0)\n");scanf("%d", &fl);if (fl)kill(getpid(), SIGINT);if (sigprocmask(SIG_BLOCK, NULL, &sigset) == -1)/* получаем сигнальную маску процесса.
Так как второйаргумент NULL, то первый аргумент игнорируется */{printf(“Ошибка при вызове sigprocmask()\n”);return -1;}else if (sigismember(&sigset, SIGINT))/*проверяем наличие сигнала SIGINT в маске*/{printf(“Сигнал SIGINT заблокирован! \n”);sigemptyset(&sigset);if (sigpending(&sigset) == -1)/*узнаем сигналы, ожидающие доставки */{printf(“Ошибка при вызове sigpending()\n”);return -1;}printf(“Сигнал SIGINT %s\n”, sigismember(&sigset,SIGINT) ? “ожидает доставки” : “не ожидаетдоставки”);/*проверяем наличие сигнала SIGINT в маске*/144}else printf(“Сигнал SIGINT не заблокирован.
\n”);return 0;}Для управления работой сигналов используется функция, аналогичная функцииsignal() в реализации обычных сигналов, но более мощная, позволяющая установитьобработку сигнала, узнать ее текущее значение, приостановить получение сигналов:#include <signal.h>int sigaction(int sig, const struct sigaction *act,struct *oact)Аргументами данного вызова являются: номер сигнала, структура, описывающаяновую реакцию на сигнал, и структура, через которую возвращается прежний методобработки сигнала.
Если процесс не интересуется прежней обработкой сигнала, он можетпередать в качестве последнего параметра NULL-указатель.Структура sigaction содержит информацию, необходимую для управлениясигналами. Ее полями являются :void (*sa_handler) (int),void (sa_sigaction) (int, siginfo_t*, void*),sigset_t sa_mask,int sa_flagsЗдесь поле sa_handler — функция-обработчик сигнала, либо константыSIG_IGN или SIG_DFL, говорящие соответственно о том, что необходимо игнорироватьсигнал или установить обработчик по умолчанию. В поле sa_mask указывается наборсигналов, которые будут добавлены к маске сигналов на время работы функцииобработчика. Сигнал, для которого устанавливается функция-обработчик, также будетзаблокирован на время ее работы.
При возврате из функции-обработчика маска сигналоввозвращается в первоначальное состояние. В последнем поле указываются флаги,модифицирующие доставку сигнала. Одним из них может быть флаг SA_SIGINFO. Еслион установлен, то при получении этого сигнала будет вызван обработчикsa_sigaction, ему помимо номера сигнала также будет передана дополнительнаяинформация о причинах получения сигнала и указатель на контекст процесса.Итак, «надежные» сигналы являются более мощным средством межпроцессноговзаимодействия нежели обычные сигналы. В частности, здесь ликвидированы такиенедостатки, как необходимость восстанавливать функцию-обработчик после получениясигнала, имеется возможность отложить получение сигнала на время выполнениякритического участка программы, имеются большие возможности получения информациио причине отправления сигнала.Пример.
Использование надежных сигналов.При получении сигнала SIGINT четырежды вызываетсяобработчик, а в пятый раз происходит обработка по умолчанию.#include <signal.h>#include <stdlib.h>#include <stdio.h>int count = 1;struct sigaction action, sa;145установленныйvoid SigHandler(int s){printf("\nI got SIGINT %d time(s)\n", count++);if (count == 5){action.sa_handler = SIG_DFL;/*изменяемсигнала */указательнафункцию-обработчикsigaction(SIGINT, &action, &sa);/* изменяем обработчик для сигнала SIGINT */}}int main(int argc, char **argv){sigset_t sigset;sigemptyset(&sigset); /* инициализируем набор сигналов*/sigaddset(&sigset,SIGINT);/*добавляемвнаборсигналов бит, соответствующий сигналу SIGINT*/if (sigprocmask(SIG_UNBLOCK, &sigset, NULL) == -1)/*устанавливаем новую сигнальную маску*/{printf(“sigprocmask() error\n”);return -1;}action.sa_handler = SigHandler;/* инициализируемсигнала*/указательнафункцию-обработчикsigaction(SIGINT, &action, &sa);/* изменяем обработчик по умолчанию для сигнала SIGINT*/while(1); /* бесконечный цикл */return 0;}3.1.3 Неименованные каналыНеименованный канал (или программный канал) представляется в виде областипамяти (на внешнем запоминающем устройстве), управляемой операционной системой,которая осуществляет выделение взаимодействующим процессам частей из этой областипамяти для совместной работы, т.е.
эта область памяти является разделяемым ресурсом.146Для доступа к неименованному каналу система ассоциирует с ним два файловыхдескриптора. Один из них предназначен для чтения информации из канала, т.е. с нимможно ассоциировать файл, открытый только на чтение. Другой дескриптор предназначендля записи информации в канал. Соответственно, с ним может быть ассоциирован файл,открытый только на запись.Дисциплина доступа к информации, записанной в программный канал, - FIFO, т.е.информация, первой записанная в канал, будет прочитана из канала первой. Это означает,что для данных файловых дескрипторов неприменимы системные вызовы перемещенияфайлового указателя. Предельный размер канала, который может быть выделенпроцессам, декларируется параметрами настройки операционной системы.Для создания неименованного канала служит системный вызов pipe().#include <unistd.h>int pipe(int *fd)Аргументом данного системного вызова является указатель на массив fd из двухцелочисленных элементов.
Если системный вызов pipe() прорабатывает успешно, то онвозвращает код ответа, равный нулю, а массив будет содержать два открытых файловыхдескриптора. Соответственно, в fd[0] будет содержаться дескриптор чтения из канала, а вfd[1] — дескриптор записи в канал. После этого с данными файловыми дескрипторамиможно использовать всевозможные средства работы с файлами, поддерживающиестратегию FIFO, т.е.
любые операции работы с файлами, за исключением тех, которыекасаются перемещения файлового указателя.Однако следует четко понимать различия между обычным файлом иканалом. Основные отличительные свойства канала следующие:- В отличие от файла, к неименованному каналу невозможен доступ по имени,т.е.
единственная возможность использовать канал – это те файловыедескрипторы, которые с ним ассоциированы- В отличие от файлов неименованный канал существует в системе, до тех пор,пока существуют процессы, его использующие. То есть для существованияканала необходим процесс, который его создал либо получил в наследство.Отметим, что канал может быть доступен как для процесса, который его создал,так и для процессов, которые унаследовали открытые файловые дескрипторы,ассоциированные с этим каналом. За счёт этой возможности наследования,неименованный канал превращается в средство взаимодействия родственныхпроцессов.- Канал – это структура данных, которая реализует модель последовательногодоступа к данным (FIFO), т.е. данные из канала можно прочитать только в тойже последовательности, в какой они были записаны в канал.
Это означает, чтодля файловых дескрипторов, ассоциированных с каналом, не определенаоперация lseek() (при попытке обратиться к этому вызову произойдетошибка).Неименованные каналы в общем случае предназначены для синхронизации иорганизации взаимодействия родственных процессов, осуществляющегося за счетпередачи по наследству ассоциированных с каналом файловых дескрипторов. Но иногдавстречаются вырожденные случаи использования неименованного канала в рамках одногопроцесса.Пример.
Использование неименованного канала. В приведенном ниже примерепроизводится копирование текстовой строки с использованием канала. Этот примерявляется «вырожденным»: он иллюстрирует случай использования канала в рамкаходного процесса – фактически осуществляется посылка данных самому себе.int main(int argc, char **argv){147char *s = “channel”;char buf[80];int pipes[2];pipe(pipes);write(pipes[1], s, strlen(s) + 1);read(pipes[0], buf, strlen(s) + 1);close(pipes[0]);close(pipes[1]);printf(“%s\n”, buf);return 0;}В приведенном примере имеется текстовая строка s, которую хотим скопировать вбуфер buf. Для этого дополнительно декларируется массив pipes, в котором будутхраниться файловые дескрипторы, ассоциированные с каналом. После обращения ксистемному вызову pipe() элемент pipes[1] хранит открытый файловый дескриптор, черезкоторый можно писать в канал, а pipes[0] — файловый дескриптор, через который можночитать из канала.
Затем происходит обращение к системному вызову write(), чтобыскопировать содержимое строки s в канал, а после этого идет обращение к системномувызову read(), чтобы прочитать данные из канала в буфер buf. Потом закрываемдескрипторы и печатаем содержимое буфера на экран.Кроме того, существует ряд отличий при организации операций чтения и записи вканал.При чтении из канала:Если из канала читается порция данных меньшая, чем находящаяся в канале, то этапорция считывается по стратегии FIFO, а оставшаяся порция непрочитанныхданных остается в канале.Если делается попытка прочитать больше данных, чем имеется в канале, и приэтом в системе имеются открытые дескрипторы записи, ассоциированные с этимканалом, то будет прочитано (т.е. изъято из канала) доступное количество данных,после чего читающий процесс блокируется до тех пор, пока в канале не появитсядостаточное количество данных для завершения операции чтения.Процесс может избежать такого блокирования, изменив для канала режимблокировки с использованием системного вызова fcntl().
В неблокирующемрежиме в ситуации, описанной выше, будет прочитано доступное количестводанных, и управление будет сразу возвращено процессу.Отметим, что блокировка происходит лишь при условии, что есть хотя бы одиноткрытый дескриптор записи в канал. Если закрывается последний дескрипторзаписи в данный канал, то в канал помещается код конца файла EOF.