Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Локальные сети персональных компьютеров. Работа с сервером Novell NetWare

© Александр Фролов, Григорий Фролов
Том 9, М.: Диалог-МИФИ, 1993, 168 стр.

[Назад] [Содеожание] [Дальше]

5.4. Семафоры

Последнее средство синхронизации процессов, которое мы рассмотрим в этой главе, - семафоры. О семафорах мы уже говорили в томе "Библиотеки системного программиста", посвященном защищенному режиму работы процессоров. Семафоры Novell NetWare - это ресурсы, расположенные физически на файл-сервере.

Программа может открыть (создать) семафор с помощью функции OpenSemaphore(), указав его имя. Функция, открывающая семафор, возвращает индекс семафора, который используется для выполнения всех операций над семафором.

С семафором помимо имени связывается некоторое число, которое может находиться в диапазоне от -127 до 127. Это число называется значением семафора.

Кроме того, для каждого семафора имеется счетчик процессов, открывших семафор. Этот счетчик увеличивает свое значение на 1, когда очередная программа открывает семафор функцией OpenSemaphore(), и уменьшает на единицу, когда одна из программ закрывает семафор функцией CloseSemaphore(). Когда счетчик принимает нулевое значение, семафор уничтожается.

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

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

Начальное значение семафора задается при его создании и обычно равно единице.

Приведем прототип функции OpenSemaphore(), открывающей семафор:

int OpenSemaphore(char *SemaphoreName,
      int InitialValue, 
      long *SemaphoreHandle, WORD *OpenCount);


Параметр SemaphoreName определяет имя открываемого семафора. Имя должно иметь длину не более 127 символов, включая закрывающий строку двоичный ноль.

Параметр InitialValue задает значение семафора, но только при первом открытии, т. е. при создании семафора. Если семафор уже создан другим процессом, этот параметр игнорируется. В качестве начального значения вы можете использовать единицу.

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

Параметр OpenCount - счетчик использования семафора. Когда очередной процесс открывает данный семафор, счетчик увеличивает свое значение на единицу.

Функция возвращает 0 при успешном завершении или код ошибки:

Код ошибки Значение
0xFE Неправильная длина имени семафора
0xFF Неправильное начальное значение семафора

Для того чтобы закрыть семафор, вам необходимо использовать функцию CloseSemaphore():

int CloseSemaphore(long SemaphoreHandle);


В качестве параметра этой функции указывается индекс закрываемого семафора.

Функция возвращает 0 при успешном завершении или код ошибки:

Код ошибки Значение
0xFF Неправильное значение индекса семафора

С помощью функции ExamineSemaphore() вы можете узнать текущее состояние семафора:

int ExamineSemaphore(long SemaphoreHandle,
      int *SemaphoreValue, WORD *OpenCount);


Для заданного первым параметра семафора функция возвращает значение семафора (параметр SemaphoreValue) и счетчик использования (параметр OpenCount).

Функция возвращает 0 при успешном завершении или код ошибки:

Код ошибки Значение
0xFF Неправильное значение индекса семафора

Перед использованием критического ресурса программа должна вызвать функцию WaitOnSemaphore(), уменьшающую значение семафора:

int WaitOnSemaphore(long SemaphoreHandle, WORD Timeout);


Параметр SemaphoreHandle определяет используемый семафор.

С помощью параметра Timeout определяется время, в течение которого функция ожидает доступность ресурса (в 18-х долях секунды).

Функция возвращает 0 при успешном завершении или код ошибки:

Код ошибки Значение
0xFE Истекло время ожидания, заданное параметром Timeout
0xFF Неправильное значение индекса семафора

Функция SignalSemaphore(), увеличивающая значение семафора, имеет следующий прототип:

int SignalSemaphore(long SemaphoreHandle);


Индекс семафора задается параметром функции.

Функция возвращает 0 при успешном завершении или код ошибки:

Код ошибки Значение
0x01 Переполнение семафора, значение семафора стало больше 127
0xFF Неправильное значение индекса семафора

Для работы с семафорами можно использовать функцию C5h прерывания INT 21h. В зависимости от содержимого регистра AL эта функция выполняет ту или иную операцию с семафором.

Открытие семафора:

На входе: AH = C5h;
AL = 00h;
DS:DX = Адрес имени семафора;
CL = Начальное значение семафора.
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

Определение состояния семафора:

На входе: AH = C5h;
AL = 01h;
CX,DX = Индекс семафора;
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок;
CX = Значение семафора;
DL = Счетчик использований семафора.

Уменьшение значения семафора:

На входе: AH = C5h;
AL = 02h;
CX,DX = Индекс семафора;
BP = Время ожидания.
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

Увеличение значения семафора:

На входе: AH = C5h;
AL = 03h;
CX,DX = Индекс семафора.
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

Закрытие семафора:

На входе: AH = C5h;
AL = 04h;
CX,DX = Индекс семафора.
На выходе: AL = Код ошибки или 0, если операция завершилась без ошибок.

5.4.1. Программа SEMSIGN

Программа SEMSIGN (листинг 25) демонстрирует использование семафоров.

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

Вы можете запустить эту программу сначала на одной станции. Затем, после того как критический ресурс будет получен, запустите эту же программу на другой станции. Так как первая программа еще не освободила ресурс, вторая не сможет получить к нему доступа.

Если вы завершите работу первой программы в течение 20 секунд, вторая программа получит доступ к ресурсу, если нет - она завершится с сообщением о том, что ресурс занят.

// ===================================================
// Листинг 25. Работа с семафорами
// Файл semsign\semsign.cpp
//
// (C) A. Frolov, 1993
// ===================================================
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <conio.h>

#define BYTE unsigned char
#define WORD unsigned int

extern "C" int GetNetWareShellVersion(char *,char *, char *);
extern "C" int OpenSemaphore(char *, int, long *, WORD *);
extern "C" int CloseSemaphore(long);
extern "C" int ExamineSemaphore(long, int *, WORD *);
extern "C" int SignalSemaphore(long);
extern "C" int WaitOnSemaphore(long, WORD);

void main(void) {

        char MajorVersion=0;
        char MinorVersion=0;
        char Revision=0;

        int ccode;
        long SemaphoreHandle;
        WORD OpenCount;
        int SemaphoreValue;

        printf("\n*SEMSIGN* (C) Frolov A., 1993\n");

// Проверяем наличие сетевой оболочки

        asm push si
        GetNetWareShellVersion(&MajorVersion,
                        &MinorVersion, &Revision);
        asm pop si

        if(MajorVersion == 0) {
                printf("\nОболочка NetWare не загружена\n");
                return;
        }

// Открываем семафор с именем SEMLOCK

        ccode = OpenSemaphore("SEMLOCK", 1, &SemaphoreHandle,
                  &OpenCount);

        if(!ccode) {
                printf("Семафор SEMLOCK открыт\n");
                printf("Handle = %ld, OpenCount = %d\n",
                         SemaphoreHandle, OpenCount);
        }
        else  {
                printf("Ошибка при открытии семафора "
                         "SEMLOCK %02.2X\n", ccode);
                return;
        }

// Определяем текущее состояние семафора

        ccode = ExamineSemaphore(SemaphoreHandle,
                  &SemaphoreValue, &OpenCount);

        if(!ccode) {
                printf("SemaphoreValue = %d\n", SemaphoreValue);
        }
        else    printf("Ошибка при получении состояния семафора "
                         "SEMLOCK %02.2X\n", ccode);

// Запрашиваем доступ к критическому ресурсу,
// ожидаем получение доступа в течение 20 секунд

        printf("Запрашиваем доступ к критическому ресурсу...\n");

        ccode = WaitOnSemaphore(SemaphoreHandle, 18*20);

        if(!ccode) {
                printf("Доступ к критическому ресурсу получен\n");
        }
        else {
                printf("Ресурс заблокирован, ошибка %02.2X\n", ccode);
                return;
        }

// Теперь, если мы получили доступ к ресурсу, можно выполнять с ним
// необходимые действия. В нашей программе мы ничего не делаем,
// просто ожидаем, пока оператор не нажмет любую клавишу

        printf("Нажмите любую клавишу для освобожения ресурса\n");
        getch();

// Освобождаем ресурс

        ccode = SignalSemaphore(SemaphoreHandle);

        if(!ccode) {
                printf("Ресурс освобожден\n");
        }
        else
                printf("Ошибка при освобождении ресурса %02.2X\n",                                     ccode);

// Закрываем семафор

        ccode = CloseSemaphore(SemaphoreHandle);

        if(!ccode)
                printf("Семафор SEMLOCK закрыт\n");
        else
                printf("Ошибка при закрытии семафора "
                         "SEMLOCK %02.2X\n", ccode);
}
[Назад] [Содеожание] [Дальше]