Спонсор: Microsoft
Опубликован: 13.11.2010 | Уровень: для всех | Доступ: свободно | ВУЗ: Санкт-Петербургский государственный университет
Лекция 12:

Методы синхронизации процессов

Семафоры как общее средство синхронизации

Наиболее простой вид синхронизации действий, выполняемых в двух процессах, - это исполнение действия B в процессе Pj после того, как действие A исполнено в процессе Pi . Рассмотрим, как такую синхронизацию осуществить с помощью семафоров.

Используем семафор flag, инициализированный 0.

Код процесса Pi:

. . . 

A;

signal (flag);

Код процесса Pj:

. . . 

wait (flag);

B;

Общие и двоичные семафоры

Из рассмотренного ясно, что имеется два вида семафоров: общий - целая переменная с теоретически неограниченным значением - и двоичный - целая переменная, значениями которой могут быть только 0 или 1. Преимуществом двоичного семафора является его возможная более простая аппаратная реализация. Например, в системах "Эльбрус" и Burroughs 5000 реализованы команды атомарного семафорного считывания с проверкой семафорного бита.

Очевидно, что общий семафор может быть реализован с помощью двоичного семафора.

Вариант операции wait (S) для системных процессов ("Эльбрус")

Для системного процесса лишние прерывания нежелательны, и может оказаться важным удерживать процессор за собой некоторое время (например, для быстрого выполнения планирования и диспетчеризации процессов). С этой целью в системе "Эльбрус" реализована, в дополнение к операции ждать (S) русифицированной версии wait(S),операция жуж(S) - "жужжать" на процессоре, т.е. ждать на закрытом семафоре, но не прерываться и не отдавать процессор, пока семафор не будет открыт операцией открыть(S)

Реализация общего семафора с помощью двоичных семафоров

Общий семафор может быть представлен тройкой из двух двоичных семафоров и целой переменной:

binary-semaphore S1 = 1;

binary-semaphore S2 = 0;

int C = начальное значение общего семафора S;

Операция wait:

wait (S1);

C--;

if (C < 0) {

	signal (S1);

	wait (S2);

}

signal (S1);

Операция signal:

wait (S1);

C++;

if (C >= 0) {

	signal (S2);

	};

signal (S1);

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

Решение классических задач синхронизации с помощью семафоров

Задача "ограниченный буфер".Имеются три классических задачи синхронизации процессов, решения которых с помощью семафоров мы рассмотрим:

  • ограниченный буфер (bounded buffer problem)
  • читатели – писатели (readers – writers problem)
  • - обедающие философы (dining philosophers problem).

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

Будем использовать три общих семафора:

semaphore full = n;

semaphore empty = 0;

semaphore mutex = 1;

Семафор full сигнализирует о переполнении буфера, empty – об исчерпании буфера, mutex используется для взаимного исключения действий над буфером.

Код процесса-производителя имеет вид:

do {

	. . . 

	сгенерировать элемент в nextp

	. . . 

	wait (full);

	wait (mutex);

	. . .

	добавить nextp к буферу

	. . . 

	signal (mutex);

	signal (empty);

} while (1);

Код процесса-потребителя:

do {

	wait (empty);

	wait (mutex);

	. . .

	взять и удалить элемент из буфера в nextc

	. . . 

	signal (mutex);

	signal (full);

	. . .

	использовать элемент из nextc

	. . .

} while (1);

Поясним использование семафоров. Семафор mutex используется "симметрично"; над ним выполняется пара операций: wait … signal – семафорные скобки. Его роль – чисто взаимное исключение критических секций. Семафор empty сигнализирует об исчерпании буфера. В начале он закрыт, так как элементов в буфере нет. Поэтому при закрытом семафоре empty потребитель вынужден ждать. Открывает семафор empty производитель, после того, как он записывает в буфер очередной элемент. Семафор full сигнализирует о переполнении буфера. В начале он равен n – максимальному числу элементов в буфере. Производитель перед записью элемента в буфер выполняет операцию wait (full),гарантируя, что, если буфер переполнен, записи нового элемента в буфер не будет. Открывает семафор full потребитель, после того, как он освободил очередной элемент буфера.

Заметим, что даже в такой сравнительно простой задаче использование семафоров весьма нетривиально и не вполне надежно – очень легко сделать ошибку. Стоит забыть открыть семафор, либо перепутать два семафора при операциях, и в программе может возникнуть взаимная блокировка процессов.

Гульжан Мурсакимова
Гульжан Мурсакимова
Василий Четвертаков
Василий Четвертаков