Главная страница Взаимодействие нетривиальных процессов /* Возвращает неотрицательный идентификатор в случае успешного завершения. -1 - в случае ошибки */ Эта функция возвращает целое значение, называемое идентификатором семафора, которое затем используется при вызове функций semop и semctl. Аргумент nsems задает количество семафоров в наборе. Если мы не создаем новый набор, а устанавливаем доступ к существующему, этот аргумент может быть нулевым. Количество семафоров в уже созданном наборе изменить нельзя. Аргумент oflag представляет собой комбинацию констант SEM R и SEM A из табл. 3.3. Здесь R обозначает Read (чтение), а А - Alter (изменение). К этим константам можно логически прибавить IPC CREAT или IPC CREAT IPC EXCL, о чем мы уже говорили в связи с рис. 3.2. При создании нового семафора инициализируются следующие поля структуры semld ds: ш поля u1d и cuid структуры sem perm устанавливаются равными действующему идентификатору пользователя процесса, а поля guid и cgid устанавливаются равными действующему идентификатору группы процесса; ш биты разрещений чтения-записи аргумента o/Zogсохраняются в sem perm .mode; ii поле sem otime устанавливается в О, а поле sem ctime устанавливается равным текущему времени; Ш значение sem nsems устанавливается равным nsems; Ш структуры sem для каждого из семафоров набора не инициализируются. Это происходит лищь при вызове semctl с командами SETVAL или SETALL. Инициализация значения семафора в комментариях к исходному коду в издании этой книги 1990 года неправильно утверждалось, что значения семафоров набора инициализируются нулем при вызове semget с созданием нового семафора. Хотя в некоторых системах это действительно происходит, гарантировать подобное поведение ядра нельзя. Более старые реализации System V вообще не инициализировали значения семафоров, оставляя их содержимое таким, каким оно было до выделения памяти. В больщинстве версий документации ничего не говорится о начальных значениях семафоров при создании нового набора. Руководство по написанию переносимых программ Х/Ореп 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 IPCJXCL SVSEMJODE: 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. SVSEMJODE): 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. Более того, даже если указан флаг 0 CREAT, семафор будет проинициализирован только в том случае, если он еще не существовал на момент вызова функции. Будет ли обсуждавшаяся выше ситуация гонок создавать какие-то проблемы - зависит от приложения. В некоторых приложениях (например, задача производителей и потребителей в листинге 10.12) единственный процесс всегда создает и инициализирует семафор. В этом варианте ситуация гонок возникать не будет. В других приложениях (пример с блокировкой файлов в листинге 10.10) нет такого единственного процесса, который бы создавал и инициализировал семафор: первый процесс, открывающий семафор, должен создать его и проинициализировать, так что в этом случае ситуацию гонок следует исключать. К счастью, существует способ исключить в данном случае ситуацию гонок. Стандарт гарантирует, что при создании набора семафоров поле sem otime структуры semid ds инициализируется нулем. (Руководства System V с давних пор говорят об этом, это утверждается и в стандартах XPG3 и Unix 98.) Это поле устанавливается равным текущему времени только при успешном вызове semop. Следовательно, второй процесс в приведенном выше примере должен просто вызвать semctl с командой IPC STAT после второго вызова semget (строка 6). Затем этот процесс должен ожидать изменения значения semjtime на ненулевое, после чего он может быть уверен в том, что семафор был успешно проинициализирован другим процессом. Это значит, что создавший семафор процесс должен проинициализировать его значение и успешно вызвать semop, прежде чем другие процессы смогут воспользоваться этим семафором. Мы используем этот метод в листингах 10.37 и 11.6. 11.3. функция semop После инициализации семафора вызовом semget с одним или несколькими семафорами набора можно выполнять некоторые действия с помощью функции semop: finclude <sys/sem.h> 1nt semopdnt semfd. struct sembuf *opsptr. s1ze t nops): I* Возвращает 0 в случае успешного завершения. -1 - в случае ошибки */ Указатель opsptr указывает на массив структур вида struct sembuf { short sem num: /* номер семафора: 0. 1 nsems-1 */ short sem op: /* операция с семафором: <0. 0. >0 */ short sem f1g: /* флаги операции: 0. IPC NOWAIT. SEM UNDO */ Количество элементов в массиве структур sembuf, на который указывает opsptr, задается аргументом nops. Каждый элемент этого массива определяет операцию с одним конкретным семафором набора. Номер семафора указывается в поле sem num и принимает значение О для первого семафора, 1 для второго и т. д., до nsems-1, где nsems соответствует количеству семафоров в наборе (второй аргумент в вызове semget при создании семафора). ПРИМЕЧАНИЕ - В структуре гарантированно содержатся только три указанных выше поля. Однако в ней могут быть и другие поля, причем порядок их может быть совершенно произвольным. Поэтому не следует статически инициализировать эту структуру кодом наподобие struct sembuf ops[2] = { 0. О, 0. /* ждем, пока первый элемент не станет равен нулю */ 0. 1. SEMJNDO /* затем увеличиваем [0] на 1 */ Вместо этого следует инициализировать ее динамически, как в нижеследующем примере: struct sembuf ops[2]: ops[0].sem num = 0: /* ждем, пока первый элемент не станет равен нулю */ ops[0].sem op - 0: ops[0].sem f1g = 0: ops[l].sem num - 0: /* затем увеличиваем [0] на 1 */ ops[l].sem op - 1: ops[l].sem f1g = SEM UNDO; Весь массив операций, передаваемый функции semop, выполняется ядром как одна операция; атомарность при этом гарантируется. Ядро выполняет все указанные операции или ни одну из них. Пример на эту тему приведен в разделе 11.5.
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |