Операционная система UNIX - страница 116

стр.

3. Если величина >semop отрицательна, процесс ожидает, пока значение семафора не станет большим или равным абсолютной величине >semop. Затем абсолютная величина semop вычитается из значения семафора.

Можно заметить, что первая операция изменяет значение семафора (безусловное выполнение), вторая операция только проверяет его значение (условное выполнение), а третья — проверяет, а затем изменяет значение семафора (условное выполнение).

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

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

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

>static struct sembuf sop_lock[2] = {

> 0, 0, 0, /* ожидать обнуления семафора */

> 0, 1, 0  /* затем увеличить значение семафора на 1 */

>};

>static struct sembuf sop_unlock[1] = {

> 0,-1, 0 /* обнулить значение семафора */

>};

Итак, для запирания ресурса процесс производит вызов:

>semop(semid, &sop_lock[0], 2);

обеспечивающий атомарное выполнение двух операций:[43]

1. Ожидание доступности ресурса. В случае, если ресурс уже занят (значение семафора равно 1), выполнение процесса будет приостановлено до освобождения ресурса (значение семафора равно 0).

2. Запирание ресурса. Значение семафора устанавливается равным 1. Для освобождения ресурса процесс должен произвести вызов:

>semop(semid, &sop_unlock[0], 1);

который уменьшит текущее значение семафора (равное 1) на 1, и оно станет равным 0, что соответствует освобождению ресурса. Если какой-либо из процессов ожидает ресурса (т. е. произвел вызов операции >sop_lock), он будет "разбужен" системой, и сможет в свою очередь запереть ресурс и работать с ним.

Во втором примере изменим трактовку значений семафора: значению 1 семафора соответствует доступность некоторого ассоциированного с семафором ресурса, а нулевому значению — его недоступность. В этом случае содержание операций несколько изменится.

>static struct sembuf sop_lock[2] = {

> 0, -1, 0, /* ожидать разрешающего сигнала (1),

>              затем обнулить семафор */

>};

>static struct sembuf sop_unlock[1] = {

> 0, 1, 0   /* увеличить значение семафора на 1 */

>};

Процесс запирает ресурс вызовом:

>semop(semid, &sop_lock[0], 1);

а освобождает:

>semop(semid, &sop_unlock[0], 1);

Во втором случае операции получились проще (по крайней мере их код стал компактнее), однако этот подход имеет потенциальную опасность: при создании семафора, его значения устанавливаются равными 0, и во втором случае он сразу же запирает ресурс. Для преодоления данной ситуации процесс, первым создавший семафор, должен вызвать операцию >sop_unlock, однако в этом случае процесс инициализации семафора перестанет быть атомарным и может быть прерван другим процессом, который, в свою очередь, изменит значение семафора. В итоге, значение семафора станет равным 2, что повредит нормальной работе с разделяемым ресурсом.

Можно предложить следующее решение данной проблемы:

>/* Создаем семафор, если он уже существует semget

>   возвращает ошибку, поскольку указан флаг IPC_EXCL */

>if ((semid = semget(key, nsems, perms | IPC_CREAT | IPC_EXCL)) < 0) {

> if (errno = EEXIST) {

>  /* Действительно, ошибка вызвана существованием объекта */

>  if ((semid = semget(key, nsems, perms)) < 0)

>   return(-1); /* Возможно, не хватает системных ресурсов */

> } else

> return(-1); /* Возможно, не хватает системных ресурсов * /

>}

>/* Если семафор создан нами, проинициализируем его */

>else

> semop(semid, &sop_unlock[0], 1);