Скачиваний:
90
Добавлен:
12.05.2015
Размер:
913.92 Кб
Скачать

19.2. Функция semget

Функция semgetсоздает набор семафоров или обеспечивает доступ к существующему набору.

#include <sys/sem.h>

int semget (key_t key, int nsems, int oflag);

/* возвращает неотрицательный идентификатор в случае успешного завершения, -1 – в случае ошибки */

Эта функция возвращает целое значение, называемое идентификатором семафора, которое затем используется при вызове функций semopиsemctl.

Аргумент nsemsзадает количество семафоров в наборе. Если мы не создаем новый набор, а устанавливаем доступ к существующему, этот аргумент может быть нулевым. Количество семафоров в уже созданном наборе изменить нельзя.

Аргумент oflagпредставляет собой комбинацию константSEM_RиSEM_Aиз табл.16.3. ЗдесьRобозначает Read (чтение), аA– Alter (изменение). К этим константам можно логически прибавитьIPC_CREATилиIPC_CREAT | IPC_EXCL, о чем мы уже говорили в связи с рис.16.2.

При создании нового семафора инициализируются следующие поля структуры semid_ds:

  • поля uidиcuidструктурыsem_permустанавливаются равными действующему идентификатору пользователя процесса, а поляgidиcgidустанавливаются равными действующему идентификатору группы процесса;

  • биты разрешений чтения-записи аргумента oflagсохраняются вsem_perm.mode;

  • поле sem_otimeустанавливается в 0, а полеsem_ctimeустанавливается равным текущему времени;

  • значение sem_nsemsустанавливается равнымnsems;

  • структуры semдля каждого из семафоров набора не инициализируются. Это происходит лишь при вызовеsemctlс командамиSETVALилиSETALL.

Инициализация значения семафора. К сожалению, значения семафоров не инициализируются при вызове функцииsemget, а устанавливаются только при вызове функцииsemctl(описание этой функции приводится ниже) с командамиSETVAL(установка значения одного из семафоров набора) иSETALL(установка значений всех семафоров набора).

Необходимость вызова двух функций для создания (semget) и инициализации (semctl) набора семафоров является неисправимым недостатком семафоров System V. Эту проблему можно решить частично, указывая флагиIPC_CREAT | IPC_EXCLпри вызовеsemget, чтобы только один процесс, вызвавшийsemgetпервым, создавал семафор и инициализировал его. Другие процессы при вызовеsemgetполучат ошибкуEEXIST, так что им придется еще раз вызватьsemget, уже не указывая флаговIPC_CREATилиIPC_EXCL.

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

1 oflag = IPC_CREAT | IPC_EXCL | SVSEM_MODE;

2 if ((semid = semget(key, 1, oflag)) >= 0)

3 { /* успешное завершение, этот процесс должен инициализировать семафор */

4 arg.val = 1;

5 semctl(semid, 0, SETVAL, arg);

6 }

7 else /* семафор уже существует, поэтому просто открываем его */

8 if (errno == EEXIST)

9 semid = semget(key, 1, SVSEM_MODE);

10 else

11 {

12 fprintf(stderr, "Ошибка вызова функции semget: %s\n", strerror(errno));

13 exit(1);

14 }

15 semop(semid, ...); /* уменьшаем значение семафора на 1 */

При этом может возникнуть следующая ситуация.

  1. Первый процесс выполняет строки 1–4, а затем останавливается ядром.

  2. Ядро запускает второй процесс, который выполняет строки 1, 2, 7, 8, 9 и 15.

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

К счастью, существует способ исключить в данном случае ситуацию гонок. Стандарт гарантирует, что при создании набора семафоров поле sem_otimeструктурыsemid_dsинициализируется нулем. Это поле устанавливается равным текущему времени только при успешном вызове функцииsemop. Следовательно, второй процесс в приведенном выше примере должен просто вызватьsemctlс командойIPC_STATпосле второго вызоваsemget(строка 9). Затем этот процесс должен ожидать изменения значения поляsem_otimeна ненулевое, после чего он может быть уверен в том, что семафор был успешно проинициализирован другим процессом. Это значит, что процесс, создавший семафор, должен проинициализировать его с помощью успешного вызова функцииsemop, прежде чем другие процессы смогут воспользоваться этим семафором. Мы используем этот метод влистингах 10.37 и 11.6.

Соседние файлы в папке Chapter.4