Опубликован: 15.06.2004 | Доступ: свободный | Студентов: 2557 / 712 | Оценка: 4.35 / 3.96 | Длительность: 27:47:00
ISBN: 978-5-9556-0011-6
Лекция 8:

Средства межпроцессного взаимодействия

Семафоры

Согласно определению стандарта POSIX-2001, семафор - это минимальный примитив синхронизации, служащий основой для более сложных механизмов синхронизации, определенных в прикладной программе.

У семафора есть значение, которое представляется целым числом в диапазоне от 0 до 32767.

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

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

Описание перечисленных функций представлено в листинге 8.31.

#include <sys/sem.h>
int semget (key_t key, int nsems, int semflg);
int semop (int semid, struct sembuf *sops, 
           size_t nsops);
int semctl (int semid, int semnum, 
            int cmd, ...);
Листинг 8.31. Описание функций для работы с семафорами.

Структура semid_ds, ассоциированная с идентификатором набора семафоров, должна содержать по крайней мере следующие поля.

struct ipc_perm sem_perm;       
/* Данные о правах доступа к 
набору семафоров */
unsigned short  sem_nsems;      
/* Число семафоров в наборе */
time_t          sem_otime;      
/* Время последней операции semop() */
time_t          sem_ctime;      
/* Время последнего изменения 
посредством semctl() */

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

unsigned short semval;  
/* Значение семафора */
pid_t          sempid;  
/* Идентификатор процесса, выполнившего 
последнюю операцию над семафором */
unsigned short semncnt; 
/* Число процессов, ожидающих увеличения 
текущего значения семафора */
unsigned short semzcnt; 
/* Число процессов, ожидающих обнуления 
значения семафора */

Функция semget() аналогична msgget() ; аргумент nsems задает число семафоров в наборе. Структура semid_ds инициализируется так же, как msqid_ds. Безымянные структуры, соответствующие отдельным семафорам, функцией semget() не инициализируются.

Операции, выполняемые посредством функции semop(), задаются массивом sops с числом элементов nsops, состоящим из структур типа sembuf, каждая из которых содержит по крайней мере следующие поля.

unsigned short sem_num; 
/* Номер семафора в наборе (нумерация с нуля) */
short          sem_op;  
/* Запрашиваемая операция над семафором */
short          sem_flg; 
/* Флаги операции */

Операция над семафором определяется значением поля sem_op: положительное значение предписывает увеличить значение семафора на указанную величину, отрицательное - уменьшить, нулевое - сравнить с нулем. Вторая операция не может быть успешно выполнена, если в результате значение семафора становится отрицательным, а третья - если значение семафора ненулевое.

Выполнение массива операций с точки зрения пользовательского процесса является неделимым действием. Это значит, во-первых, что если операции выполняются, то только все вместе и, во-вторых, никакой другой процесс не может получить доступ к промежуточному состоянию набора семафоров, когда часть операций из массива уже выполнилась, а другая еще не успела. Операционная система, разумеется, выполняет операции из массива по очереди, причем порядок не оговаривается. Если очередная операция не может быть выполнена, то эффект предыдущих аннулируется, а вызов функции semop() приостанавливается ( операция с блокировкой ) или немедленно завершается неудачей ( операция без блокировки ). Подчеркнем, что в случае неудачного завершения вызова semop() значения всех семафоров в наборе останутся неизменными.

Приведенный в листинге 8.32 массив операций задает уменьшение (с блокировкой) семафора 1 при условии, что значение семафора 0 равно нулю.

sembuf [0].sem_num = 1;
sembuf [0].sem_flg = 0;
sembuf [0].sem_op = -2;

sembuf [1].sem_num = 0;
sembuf [1].sem_flg = IPC_NOWAIT;
sembuf [1].sem_op =  0;
Листинг 8.32. Пример задания массива операций над семафорами.

Обращаясь к функции semctl(), процессы могут получать информацию о состоянии набора семафоров, изменить ряд его характеристик, удалить набор.

Аргументы semid ( идентификатор набора семафоров ) и semnum (номер семафора в наборе) определяют объект, над которым выполняется управляющее действие, задаваемое значением аргумента cmd. Если объектом является набор, значение semnum игнорируется.

Для некоторых действий задействован четвертый аргумент (см. листинг 8.33).

union semun {
 int val;
 struct semid_ds *buf;
 unsigned short *array;
} arg;
Листинг 8.33. Описание четвертого (дополнительного) аргумента функции semctl().

Среди допустимых действий - GETVAL (получить значение семафора и выдать его в качестве результата) и SETVAL (установить значение семафора равным arg.val ). Имеются и аналогичные групповые действия - GETALL (прочитать значения всех семафоров набора и поместить их в массив arg.array ) и SETALL (установить значения всех семафоров набора равными значениям элементов массива). Предусмотрены действия, позволяющие выяснить идентификатор процесса, выполнившего последнюю операцию над семафором ( GETPID ), а также число процессов, ожидающих увеличения / обнуления ( GETNCNT / GETZCNT ) значения семафора (информация о процессах выдается в качестве результата, см. листинг 8.34).

val = semctl (semid, semnum, GETVAL);

arg.val = ...;
if (semctl (semid, semnum, SETVAL, arg) == -1) ...;

arg.array = (unsigned short *) malloc (nsems * sizeof (unsigned short));
err = semctl (semid, 0, GETALL, arg);

for (i = 0; i < nsems; i++) arg.array [i] = ...;
err = semctl (semid, 0, SETALL, arg);

lpid = semctl (semid, semnum, GETPID);

ncnt = semctl (semid, semnum, GETNCNT);

zcnt = semctl (semid, semnum, GETZCNT);
Листинг 8.34. Примеры управляющих действий над семафорами.

Наконец, для семафоров, как и для очередей сообщений, определены управляющие команды IPC_STAT (получить информацию о состоянии набора семафоров ), IPC_SET (переустановить характеристики), IPC_RMID (удалить набор семафоров ), представленные в листинге 8.35.

arg.buf = (struct semid_ds *) malloc (sizeof (struct semid_ds);
err = semctl (semid, 0, IPC_STAT, arg);
arg.buf->sem_perm.mode = 0644;
err = semctl (semid, 0, IPC_SET, arg);
Листинг 8.35. Дополнительные примеры управляющих действий над семафорами.
Антон Коновалов
Антон Коновалов

В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13
Планируется ли актуализация материалов данного очень полезного курса?