Главная страница  Взаимодействие нетривиальных процессов 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 [ 96 ] 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

/* Возвращает неотрицательный идентификатор в случае успешного завершения. -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.



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 [ 96 ] 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.