Вход/Регистрация
UNIX: взаимодействие процессов
вернуться

Стивенс Уильям Ричард

Шрифт:

В большинстве версий документации ничего не говорится о начальных значениях семафоров при создании нового набора. Руководство по написанию переносимых программ X/Open XPG3 (1989) и стандарт Unix 98 исправляют это упущение и открыто утверждают, что значения семафоров не инициализируются вызовом 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 arg.val = 1;

4 Semctl(semid, 0, SETVAL, arg);

5 } else if (errno == EEXIST) { /* уже существует, поэтому просто открываем семафор */

6 semid = Semget(key, 1, SVSEM_MODE);

7 } else

8 err_sys("semget error");

9 Semop(semid, …); /* уменьшаем значение семафора на 1 */

При этом может произойти вот что:

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

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

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

ПРИМЕЧАНИЕ

В семафорах Posix эта проблема исключается благодаря тому, что семафоры создаются и инициализируются единственным вызовом — sem_open. Более того, даже если указан флаг O_CREAT, семафор будет проинициализирован только в том случае, если он еще не существовал на момент вызова функции.

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

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

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 при создании семафора).

  • Читать дальше
  • 1
  • ...
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: