Чтение онлайн

ЖАНРЫ

Программирование для Linux. Профессиональный подход

Самьюэл Алекс

Шрифт:

Кроме того, чтобы несколько процессов могли совместно работать с общим сегментом, они должны "договориться" о выборе одинакового ключа.

5.2. Семафоры для процессов

Как говорилось в предыдущем разделе, процессы должны координировать свои усилия при совместном доступе к памяти. Вспомните: в разделе 4.4.5, "Обычные потоковые семафоры", рассказывалось о семафорах, которые являются счетчиками, позволяющими синхронизировать работу потоков. В Linux имеется альтернативная реализация семафоров (иногда называемых семафорами System V), предназначенных для синхронизации процессов. Такие семафоры выделяются, используются и освобождаются подобно совместно используемым

сегментам памяти. Для большинства случаев достаточно одного семафора, тем не менее они работают группами. В этом разделе мы опишем системные вызовы, позволяющие реализовать двоичный семафор.

5.2.1. Выделение и освобождение семафоров

Функции

semget
и
semctl
выделяют и освобождают семафоры, функционируя подобно функциям
shmget
и
shmctl
. Первым аргументом функции
semget
является ключ, идентифицирующий группу семафоров; второй аргумент — это число семафоров в группе; третий аргумент — флаги прав доступа, как в функции
shmget
. Функция
semget
возвращает идентификатор группы семафоров. Если задан ключ, принадлежащий существующей группе, будет возвращен ее идентификатор. В этом случае второй аргумент (число семафоров) может равняться нулю.

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

semctl
, передав ей идентификатор группы, число семафоров в группе, флаг
IPC_RMID
и произвольное значение типа
union semun
(оно игнорируется). Значение EUID (эффективный идентификатор пользователя) процесса, вызвавшего функцию, должно совпадать с аналогичным значением процесса, создавшего группу семафоров (либо вызывающий процесс должен быть запущен пользователем
root
). В отличие от совместно используемых сегментов памяти, удаляемая группа семафоров немедленно освобождается.

В листинге 5.2 представлены функции, выделяющие и освобождающие двоичный семафор.

Листинг 5.2. (sem_all_deall.c) Выделение и освобождение двоичного семафора

#include <sys/ipc.h>

#include <sys/sem.h>

#include <sys/types.h>

/* Тип union semun необходимо определить самостоятельно. */

union semun {

 int val;

 struct semid_ds *buf;

 unsigned short int* array;

 struct seminfo *__buf;

};

/* Получаем идентификатор семафора и создаем семафор,

если идентификатор оказывается уникальным. */

int binary_semaphore_allocation(key_t key, int sem_flags) {

 return semget(key, 1, sem_flags);

}

/* Освобождаем семафор, подразумевая, что пользователи

больше не работают с ним. В случае ошибки

возвращается -1. */

int binary_semaphore_deallocate(int semid) {

 union semun ignored_argument;

 return semctl(semid, 1, IPC_RMID, ignored_argument}

}

5.2.2.

Инициализация семафоров

Выделение и инициализация семафора — две разные операции. Чтобы проинициализировать семафор, вызовите функцию

semctl
, задав второй аргумент равным нулю, а третий аргумент — равным константе
SETALL
. Четвертый аргумент должен иметь тип
union semun
, поле
array
которого указывает на массив значений типа
unsigned short
. Каждое значение инициализирует один семафор из набора.

В листинге 5.3 представлена функция, инициализирующая двоичный семафор.

Листинг 5.3. (sem_init.c) Инициализация двоичного семафора

#include <sys/types.h>

#include <sys/ipc.h>

#include <sys/sem.h>

/* Тип union semun необходимо определить самостоятельно. "/

union semun {

 int val;

 struct semid_ds* buf;

 unsigned short int *array;

 struct seminfo *__buf;

};

/* Инициализация двоичного семафора значением 1. */

int binary_semaphore_initialize(int semid) {

 union semun argument;

 unsigned short values(1);

 values[0] = 1;

 argument.array = values;

 return semctl(semid, 0, SETALL, argument);

}

5.2.3. Операции ожидания и установки

Каждый семафор имеет неотрицательное значение и поддерживает операции ожидания и установки. Системный вызов

semop
реализует обе операции. Первым аргументом функции является идентификатор группы семафоров. Второй аргумент — это массив значений типа
struct sembuf
, задающих выполняемые операции. Третий аргумент — это длина массива.

Ниже перечислены поля структуры

sembuf
.

■ 

sem_num
— номер семафора в группе.

■ 

sem_op
— число, задающее операцию.

Если данное поле содержит положительное число, оно немедленно добавляется к значению семафора.

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

Если данное поле равно нулю, операция блокируется до тех пор, пока значение семафора не станет равным нулю.

■ 

sem_flg
— это значение флага. Флаг
IPC_NOWAIT
предотвращает блокирование операции. Если запрашиваемая операция приведет к блокированию, функция
semop
завершится выдачей кода ошибки. При наличии флага
SEM_UNDO
ОС Linux автоматически отменит выполненную операцию по завершении процесса.

В листинге 5.4 иллюстрируются операции ожидания и установки двоичного семафора.

Поделиться с друзьями: