2011. Машбук (1114722), страница 48
Текст из файла (страница 48)
Подробнее процесс созданияразделяемого ресурса описан выше. Отметим семантику прав доступа к такому типуразделяемых ресурсов, как семафоры: процесс, имеющий право доступа к массивусемафоров по чтению, может проверять значение семафоров; процесс, имеющий праводоступа по записи, может как проверять, так и изменять значения семафоров.177В случае, если среди флагов указан IPC_CREAT, аргумент nsems долженпредставлять собой положительное число, если же этот флаг не указан, значение nsemsигнорируется.
Отметим, что в заголовочном файле <sys/sem.h> определена константаSEMMSL, задающая максимально возможное число семафоров в наборе. Если значениеаргумента nsems больше этого значения, вызов semget() завершится неудачно.В случае успеха вызов semget() возвращает положительный дескрипторсозданного разделяемого ресурса, в случае неудачи -1.3.2.3.2 Операции над семафоромИспользуя полученный дескриптор, можно производить изменять значения одногоили нескольких семафоров в наборе, а также проверять их значения на равенство нулю,для чего используется системный вызов semop():#include <sys/types.h>#include<sys/ipc.h>#include<sys/sem.h>int semop (int semid, struct sembuf *semop, size_t nops)Этому вызову передаются следующие аргументы:semid – дескриптор массива семафоров;semop – массив из объектов типа struct sembuf, каждый из которых задаетодну операцию над семафором;nops – длина массива semop.
Количество семафоров, над которыми процессможет одновременно производить операцию в одном вызове semop(), ограниченоконстантой SEMOPM, описанной в файле <sys/sem.h>. Если процесс попытаетсявызвать semop() с параметром nops, большим этого значения, этот вызов вернетнеуспех.Структура имеет sembuf вид:struct sembuf {short sem_num; /* номер семафора в векторе */short sem_op;/* производимая операция */short sem_flg; /* флаги операции */}Поле операции в структуре интерпретируется следующим образом. Пусть значениесемафора с номером sem_num равно sem_val.1.
если значение операции не равно нулю: оценивается значение суммы sem_val + sem_op. если эта сумма больше либо равна нулю, то значение данногосемафора устанавливается равным этой сумме: sem_val =sem_val + sem_op если же эта сумма меньше нуля, то действие процесса будетприостановлено до тех пор, пока значение суммы sem_val +sem_op не станет больше либо равно нулю, после чего значениесемафора устанавливается равным этой сумме: sem_val =sem_val + sem_op2. Если код операции sem_op равен нулю: Если при этом значение семафора (sem_val) равно нулю,происходит немедленный возврат из вызова Иначе происходит блокирование процесса до тех пор, пока значениесемафора не обнулится, после чего происходит возврат из вызова178Таким образом, ненулевое значение поля sem_op обозначает необходимостьприбавить к текущему значению семафора значение sem_op, а нулевое – дождатьсяобнуления семафора.
Итак, порядок работы с семафором можно записать в видеследующей схемы.Если 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;}Отметим, что данный пример демонстрирует два разных приема использованиясемафоров для синхронизации: первый процесс блокируется в ожидании обнулениясемафора, т.е.