Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Семафоры,мониторы.doc
Скачиваний:
21
Добавлен:
29.08.2019
Размер:
129.54 Кб
Скачать

5.3 Простое распределение ресурсов при помощи мониторов

Предположим, что нескольким процессам необходим доступ к определенному ресурсу, который может быть использован в каж­дый конкретный момент времени только одним процессом. На рис. 5.1 приведен простой монитор-распределитель ресурсов, обес­печивающий выделение и освобождение подобного ресурса;

monitor распределительресурсов;

var ресурсзанят: логический;

ресурссвободен: условие;

procedure захватитьресурс;

begin

if ресурсзанят then

wait(pecypccвoбoдeн);

ресурсзанят := истина

end;

procedure возвратитьресурс;

begin

ресурсзанят := ложь;

signal(ресурссвободен)

end;

begin

ресурсзанят := ложь

end;

Рис. 5.1 Простое распределене ресурса при помощи монитора.

Достоинство подобного распределителя ресурсов заключается в том, что он работает точно так же, как двоичный семафор: команда "захватитьресурс" выполняет функцию Р-оператора, а команда "возвратитьресурс" – V-оператора. Поскольку с помощью такого простого монитора с одним ресурсом можно реализовать семафор, мониторы по меньшей мере обладают той же логической мощностью, что и семафоры.

5.4 Пример монитора: кольцевой буфер

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

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

В операционных системах часто предусматривается выделение некоторого фиксированного количества ячеек памяти для исполь­зования в качестве буфера передач между процессом-производи­телем и процессом-потребителем. Этот буфер можно представить в виде массива заданного размера. Процесс-производитель помещает передаваемые данные в последовательные элементы этого массива. Процесс-потребитель считывает данные в том порядке, в котором они помещались. Производитель может опережать потребителя на несколько шагов. Со временем процесс-производитель займет по­следний элемент массива. Когда он сформирует после этого очеред­ные данные для передачи, он должен будет "возвратиться к исходной точке" и снова начать с записи данных в первый элемент массива (при этом естественно предполагается, что потребитель уже про­читал те данные, которые ранее сюда поместил производитель). Такой массив по сути работает как замкнутое кольцо, поэтому и носит название кольцевого буфера.

Поскольку размер кольцевого буфера ограничен, процесс-про­изводитель в какой-то момент может столкнуться с тем, что все элементы массива окажутся занятыми; в этом случае процессу-производителю необходимо будет подождать, пока потребитель не прочитает и тем самым не освободит хотя бы один элемент массива. Аналогично может возникнуть ситуация, когда потребитель хотел бы прочитать данные, а массив оказывается пустым; в этом случае потребитель должен будет ждать, пока процесс-производитель не поместит данные в массив. На рис. 5.2 приведен монитор под назва­нием "мониторкольцевогобуфера", который реализует кольцевой буфер и соответствующий механизм синхронизации для обеспечения взаимодействия в паре "производитель-потребитель" (базовую вер­сию этого монитора предложил Хоор (Но 74)).

monitor мониторкольцевогобуфера;

var кольцевойбуфер: array [0.. размербуфера1] of тип;

текущая позиция: 0.. размербуфера;

очереднаязаполняемаяпозиция: 0.. размербуфера – 1;

очереднаяосвобождаемаяпозиция: 0..размербуфера – 1;

буфернепуст, буфернезаполнен: условие;

procedure заполнитьпозицию(данные: тип);

begin

if текущая позиция = размербуфера

then wait(буфернезаполнен);

кольцевойбуфер [очереднаязаполняемаяпозиция]: = данные;

текущаяпозиция:= текущая позиция + 1;

очереднаязаполняемаяпозиция := (очереднаязаполняемая­позиция + 1) mod размербуфера;

signal(бyфepнeпycт)

end;

procedure освободитьпозицию(var данные:тип);

begin

if текущаяпозиция = 0 then wait(буфернепуст);

данные := кольцевойбуфер [очереднаяосвобождаемаяпози­ция];

текущаяпозиция := текущаяпозиция – 1;

очереднаяосвобождаемаяпозиция := (очереднаяосвобождае­мая-

позиция + 1) mod размербуфера;

signal (буфернезаполнен)

end;

begin

текущаяпозиция := 0;

очереднаязаполняемаяпозиция := 0;

очереднаяосвобождаемаяпозиция := 0

end;

Рис. 5.2 Реализация кольцевого буфера при помощи монитора.

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

Условие "буфернезаполнен" является признаком, которого ждет процесс-производи­тель, если обнаружит, что кольцевой буфер целиком заполнен; этот признак устанавливается по сигналу процесса-потребителя о том, что он только что освободил позицию.

Условие "буфернепуст" является признаком, которого ждет процесс-потребитель, если об­наружит, что кольцевой буфер пуст; этот признак устанавливается по сигналу процесса-производителя о том, что он только что по­местил данные в некоторую позицию.

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

Пер­вый процесс, записывающий данные в буфер, обычно называется спулером.

Другой процесс читает строки данных из кольцевого буфера и выдает их на принтер. Однако этот второй процесс, обычно называемый деспулером, работает с гораздо меньшей скоростью – со скоростью принтера.

Кольцевой буфер должен иметь достаточно большой размер, чтобы "выбирать слабину", которая возникает из-за несоответствия скоростей работы спулера и деспулера.