Операционные системы 2011 (1114689), страница 49
Текст из файла (страница 49)
Итак, порядок работы с семафором можно записать в видеследующей схемы.Если sem_op = 0 тоесли sem_val ≠ 0 топока (sem_val ≠ 0) [процесс блокирован][возврат из вызова]Если sem_op ≠ 0 тоесли sem_val + sem_op < 0 топока (sem_val+sem_op< 0) [процесс блокирован]sem_val = sem_val + sem_opПоле sem_flg в структуре sembuf содержит комбинацию флагов, влияющих навыполнение операции с семафором. В этом поле может быть установлен флагIPC_NOWAIT, который предписывает соответствующей операции над семафором неблокировать процесс, а сразу возвращать управление из вызова semop(). Вызовsemop() в такой ситуации вернет –1.
Кроме того, в этом поле может быть установленфлаг SEM_UNDO, в этом случае система запомнит изменение значения семафора,произведенные данным вызовом, и по завершении процесса автоматически ликвидируетэто изменение. Это предохраняет от ситуации, когда процесс уменьшил значениесемафора, начав работать с ресурсом, а потом, не увеличив значение семафора обратно, покакой-либо причине завершился. В этом случае остальные процессы, ждущие доступа кресурсу, оказались бы заблокированы навечно.3.2.3.3 Управление состоянием массива семафоров#include <sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>int semctl (int semid, int num, int cmd, union semunarg)С помощью этого системного вызова можно запрашивать и изменять управляющиепараметры разделяемого ресурса, а также удалять его.Первый параметр вызова – дескриптор массива семафоров.
Параметр numпредставляет собой индекс семафора в массиве, параметр cmd задает операцию, котораядолжна быть выполнена над данным семафором. Последний аргумент имеет тип unionsemun и используется для считывания или задания управляющих параметров одногосемафора или всего массива, в зависимости от значения аргумента cmd. Тип данныхunion semun определен в файле <sys/sem.h> и выглядит следующим образом:union semun {int val; /* значение одного семафора */struct semid_ds *buf; /* параметры массива семафоров вцелом */ushort*array;/* массив значений семафоров */}где struct semid_ds – структура, описанная в том же файле, в полях которойхранится информация о всем наборе семафоров в целом, а именно, количество семафоровв наборе, права доступа к нему и статистика доступа к массиву семафоров.179Приведем некоторые наиболее часто используемые значения аргумента cmd:IPC_STAT – скопировать управляющие параметры набора семафоров по адресуarg.bufIPC_SET – заменить управляющие параметры набора семафоров на те, которыеуказаны в arg.buf.
Чтобы выполнить эту операцию, процесс должен быть владельцемили создателем массива семафоров, либо обладать правами привилегированногопользователя, при этом процесс может изменить только владельца массива семафоров иправа доступа к нему.IPC_RMID – удалить массив семафоров.
Чтобы выполнить эту операцию, процессдолжен быть владельцем или создателем массива семафоров, либо обладать правамипривилегированного пользователяGETALL, SETALL – считать / установить значения всех семафоров в массив, накоторый указывает arg.arrayGETVAL – возвратить значение семафора с номером num. Последний аргументвызова игнорируется.SETVAL – установить значение семафора с номером num равным arg.valВ случае успешного завершения вызов возвращает значение, соответствующееконкретной выполнявшейся операции (0, если не оговорено иное), в случае неудачи – -1.Пример.
Работа с разделяемой памятью с синхронизацией семафорами.В рассматриваемом примере моделируется двухпроцессная система, в которойпервый процесс создает ресурсы разделяемая память и массив семафоров. Затем онначинает читать информацию со стандартного устройства ввода, считанные строкизаписываются в разделяемую память. Второй процесс читает строки из разделяемойпамяти.
Таким образом, мы имеем критический участок в момент, когда один процесс ещене дописал строку, а другой ее уже читает. Данная задача требует синхронизации, котораябудет осуществляться на основе механизма семафоров. Стоит обратить внимание на то,что с одним и тем же ключом одновременно создаются ресурсы двух разных типов (вслучае использования ресурсов одного типа подобные действия некорректны).1й процесс:#include <stdio.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include <string.h>#define NMAX256int main(int argc, char **argv){key_t key;int semid, shmid;struct sembuf sops;char *shmaddr;char str[NMAX];key = ftok(“/usr/ter/exmpl”, ’S’);/* создаем уникальный ключ */semid = semget(key, 1, 0666 | IPC_CREAT);/* создаем один семафор с определенными правами доступа*/shmid = shmget(key, NMAX, 0666 | IPC_CREAT);/* создаем разделяемую память на 256 элементов */180shmaddr = shmat(shmid, NULL, 0);/*подключаемсякразделупамяти,вshaddr–указатель на буфер с разделяемой памятью */semctl(semid,0,SETVAL, (int) 0);/* инициализируем семафор значением 0 */sops.sem_num = 0;sops.sem_flg = 0;do { /* запуск цикла */printf(“Введите строку:”);if (fgets(str, NMAX, stdin) == NULL){/* окончание ввода *//* пишем признак завершения – строку “Q” */strcpy(str, “Q”);}/* в текущий момент семафор открыт для этогопроцесса */strcpy(shmaddr, str); /* копируем строку в разд.память *//* предоставляем второму процессу возможностьвойти */sops.sem_op = 3; /* увеличение семафора на 3 */semop(semid, &sops, 1);/* ждем, пока семафор будет открыт для 1гопроцесса - для следующей итерации цикла */sops.sem_op = 0; /* ожидание обнуления семафора */semop(semid, &sops, 1);} while (str[0] != ‘Q’);/* в данный момент второй процесс уже дочитал изразделяемой памяти и отключился от нее – можно ееудалять*/shmdt(shmaddr) ; /* отключаемся от разделяемой памяти*/shmctl(shmid, IPC_RMID, NULL);/* уничтожаем разделяемую память */semctl(semid, 0, IPC_RMID, (int) 0);/* уничтожаем семафор */return 0;}2й процесс:/* необходимо корректно определить существование ресурса,если он есть - подключиться */#include <stdio.h>#include <sys/types.h>#include <sys/ipc.h>#include <sys/sem.h>#include <string.h>#define NMAX256int main(int argc, char **argv){key_t key;181int semid, shmid;struct sembuf sops;char *shmaddr;char str[NMAX];key = ftok(“/usr/ter/exmpl”,’S’);/* создаем тот же самый ключ */semid = semget(key, 1, 0666 | IPC_CREAT);shmid = shmget(key, NMAX, 0666 | IPC_CREAT);/* аналогично предыдущему процессу- инициализацииресурсов */shmaddr = shmat(shmid, NULL, 0);sops.sem_num = 0;sops.sem_flg = 0;/* запускаем цикл */do{printf(“Waiting… \n”); /* ожидание на семафоре */sops.sem_op = -2;/* будем ожидать, пока “значение семафора” +”значение sem_op” не станет положительным*/semop(semid, &sops, 1);/* теперь значение семафора равно 1 */strcpy(str, shmaddr); /* копируем строку изразд.памяти *//*критическая секция - работа с разделяемойпамятью - в этот моментпервый процесс кразделяемой памяти доступа не имеет*/if (str[0] == ‘Q’){/*завершениеработы–освобождаемразделяемую память */shmdt(shmaddr);}/*после работы – обнулим семафор*/sops.sem_op=-1;semop(semid, &sops, 1);printf(“Read from shared memory: %s\n”, str);} while (str[0] != ‘Q’);return 0;}Отметим, что данный пример демонстрирует два разных приема использованиясемафоров для синхронизации: первый процесс блокируется в ожидании обнулениясемафора, т.е.
для того, чтобы он мог войти в критическую секцию, значение семафорадолжно стать нулевым; второй процесс блокируется при попытке уменьшить значениесемафора до отрицательной величины, для того, чтобы этот процесс мог войти вкритическую секцию, значение семафора должно быть не менее 3.
Обратим внимание, чтов данном примере, помимо взаимного исключения процессов, достигается строгаяпоследовательность действий двух процессов: они получают доступ к критической секциистрого по очереди.1823.3Сокеты — унифицированный интерфейспрограммирования распределенных системСредства межпроцессного взаимодействия ОС UNIX, представленные в системеIPC, решают проблему взаимодействия процессов, выполняющихся в рамках однойоперационной системы. Однако, очевидно, их невозможно использовать, когда требуетсяорганизовать взаимодействие процессов в рамках сети. Это связано как с принятойсистемой именования, которая обеспечивает уникальность только в рамках даннойсистемы, так и вообще с реализацией механизмов разделяемой памяти, очередисообщений и семафоров, – очевидно, что для удаленного взаимодействия они не годятся.Следовательно, возникает необходимость в каком-то дополнительном механизме,позволяющем общаться двум процессам в рамках сети.
Однако если разработчикипрограмм будут иметь два абсолютно разных подхода к реализации взаимодействияпроцессов, в зависимости от того, на одной машине они выполняются или на разных узлахсети, им, очевидно, придется во многих случаях создавать два принципиально разныхкуска кода, отвечающих за это взаимодействие.
Понятно, что это неудобно и хотелось быв связи с этим иметь некоторый унифицированный механизм, который в определеннойстепени позволял бы абстрагироваться от расположения процессов и давал бывозможность использования одних и тех же подходов для локального и нелокальноговзаимодействия. Кроме того, как только мы обращаемся к сетевому взаимодействию,встает проблема многообразия сетевых протоколов и их использования.
Понятно, чтобыло бы удобно иметь какой-нибудь общий интерфейс, позволяющий пользоватьсяуслугами различных протоколов по выбору пользователя.Обозначенные проблемы был призван решить механизм, впервые появившийся вUNIX – BSD (4.2) и названный сокетами (sockets).Сокеты представляют собой в определенном смысле обобщение механизмаканалов, но с учетом возможных особенностей, возникающих при работе в сети. Крометого, они предоставляют больше возможностей по передаче сообщений, например, могутподдерживать передачу экстренных сообщений вне общего потока данных. Общая схемаработы с сокетами любого типа такова: каждый из взаимодействующих процессов долженна своей стороне создать и отконфигурировать сокет, после чего процессы должныосуществить соединение с использованием этой пары сокетов. По окончаниивзаимодействия сокеты уничтожаются.Механизм сокетов чрезвычайно удобен при разработке взаимодействующихприложений, образующих систему «клиент-сервер».