В настоящее время актуальный стандарт - это POSIX 2008 и его дополнение POSIX 1003.13 |
Средства межпроцессного взаимодействия
Семафоры
Согласно определению стандарта 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. Дополнительные примеры управляющих действий над семафорами.