Книга: UNIX: взаимодействие процессов

11.3. Функция semop

11.3. Функция semop

После инициализации семафора вызовом semget с одним или несколькими семафорами набора можно выполнять некоторые действия с помощью функции semop:

#include <sys/sem.h>
int semop(int semid, struct sembuf *opsptr, size_t nops);
/* Возвращает 0 в случае успешного завершения, –1 – в случае ошибки */

Указатель opsptr указывает на массив структур вида

struct sembuf {
 short sem_num; /* номер семафора: 0, 1,… nsems-1 */
 short sem_op; /* операция с семафором: <0, 0, >0 */
 short sem_flg; /* флаги операции: 0, IPC_NOWAIT, SEM_UNDO */
};

Количество элементов в массиве структур sembuf, на который указывает opsptr, задается аргументом nops. Каждый элемент этого массива определяет операцию с одним конкретным семафором набора. Номер семафора указывается в поле sen_num и принимает значение 0 для первого семафора, 1 для второго и т. д., до nsems-1, где nsems соответствует количеству семафоров в наборе (второй аргумент в вызове semget при создании семафора).

ПРИМЕЧАНИЕ

В структуре гарантированно содержатся только три указанных выше поля. Однако в ней могут быть и другие поля, причем порядок их может быть совершенно произвольным. Поэтому не следует статически инициализировать эту структуру кодом наподобие

struct sembuf ops[2] = {
 0, 0, 0, /* ждем, пока первый элемент не станет равен нулю */
 0, 1, SEM_UNDO /* затем увеличиваем [0] на 1 */
};

Вместо этого следует инициализировать ее динамически, как в нижеследующем примере:

struct sembuf ops[2];
ops[0].sem_num = 0; /* ждем, пока первый элемент не станет равен нулю */
ops[0].sem_op = 0;
ops[0].sem_flg = 0;
ops[1].sem_num = 0; /* затем увеличиваем [0] на 1 */
ops[1].sem_op = 1;
ops[1].sem_flg = SEM_UNDO;

Весь массив операций, передаваемый функции semop, выполняется ядром как одна операция; атомарность при этом гарантируется. Ядро выполняет все указанные операции или ни одну из них. Пример на эту тему приведен в разделе 11.5. 

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

? semval — текущее значение семафора (рис. 11.1);

? semncnt — количество потоков, ожидающих, пока значение семафора не станет больше текущего (рис. 11.1);

? semzcnt — количество потоков, ожидающих, пока значение семафора не станет нулевым (рис. 11.1);

? semadj — корректировочное значение данного семафора для вызвавшего процесса. Это значение обновляется, только если для данной операции указан флаг SEM_UNDO в поле sem_flg структуры sembuf. Эта переменная создается в ядре для каждого указавшего флаг SEM_UNDO процесса в отдельности; поле структуры с именем semadj не обязательно должно существовать;

? когда выполнение потока приостанавливается до завершения операции с семафором (мы увидим, что поток может ожидать либо обнуления семафора, либо получения семафором положительного значения), поток перехватывает сигнал и происходит возвращение из обработчика сигнала, функция semop возвращает ошибку EINTR. Используя терминологию, введенную в книге [24, с. 124], можно сказать, что функция semop представляет собой медленный системный вызов, который прерывается перехватываемыми сигналами;

? когда выполнение потока приостанавливается до завершения операции с семафором и этот семафор удаляется из системы другим потоком или процессом, функция semop возвращает ошибку EIDRM (identifier removed — идентификатор удален).

Опишем теперь работу функции semop в зависимости от трех возможных значений поля sem_op: отрицательного, нулевого и положительного.

1. Если значение sem_op положительно, оно добавляется к semval. Такое действие соответствует освобождению ресурсов, управляемых семафором. Если указан флаг SEM_UNDO, значение sem_op вычитается из значения semadj данного семафора.

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

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

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

Если значение semval больше либо равно модулю sem_op, модуль sem_op вычитается из semval. Если указан флаг SEM_UNDO, модуль sem_op добавляется к значению поля semadj данного семафора.

Если значение semval меньше модуля sem_op, значение поля semncnt данного семафора увеличивается, а вызвавший поток блокируется до тех пор, пока semval не станет больше либо равно модулю semop. Когда это произойдет, поток будет разблокирован, а модуль sem_op будет отнят от semval и из значения semncnt будет вычтена единица. Если указан флаг SEM_UNDO, модуль sem_op добавляется к значению поля semadj данного семафора. Как отмечалось ранее, поток не будет приостановлен, если указан флаг IPC_NOWAIT. Ожидание завершается преждевременно, если перехватываемый сигнал вызывает прерывание либо семафор удаляется другим потоком.

ПРИМЕЧАНИЕ

Если сравнить этот набор операций с теми, которые разрешены для семафоров Posix, мы увидим, что для последних определены только команды –1 (sem_wait) и +1 (sem_post). Для семафоров System V значение семафора может изменяться с шагом, отличным от 1, и кроме того, поток может ожидать, чтобы значение семафора стало нулевым. Эти операции являются более общими, что вместе с возможностью включения нескольких семафоров в набор делает семафоры System V более сложными, чем одиночные семафоры Posix.

Оглавление книги


Генерация: 0.852. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз