Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекция по ОС.doc
Скачиваний:
2
Добавлен:
19.11.2018
Размер:
235.52 Кб
Скачать
    1. Блокировка памяти.

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

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

Проблема синхронизации легко решается, если потребовать, чтобы процессы ПР1 и ПР2 входили в свои критические интервалы попеременно. Для этого можно использовать одну общую переменную, которая будет хранить указатель того, чья очередь войти в критическую секцию. Текст такого решения на языке, близком к Pascal, приведен в Листинге 6.1.

    1. Первый вариант попытки реализации взаимного исключения.

Var

перекл : Integer;

Begin

перекл :=1;

ParBegin

While true do

Begin

while перекл = 2 do

begin

end;

CS1: {Критическая секция процесса PR1}

перекл:=2;

PR1: {Оставшаяся часть процесса PR1}

End;

And

While true do

Begin

while перекл = 1 do

begin

end;

CS2: {Критическая секция процесса PR2}

перекл:=1;

PR2: {Оставшаяся часть процесса PR2}

End;

ParEnd;

End.

Здесь и далее конструкция следующего типа

Parbegin

Sll: S12; ... ; S1N1;

and... S21; S22: ... ; S2N2

and... SK1: SK2; ... ; SKNk

Parend

означает параллельность К описываемых последовательных процессов. Конструкция из операторов S11; S12; ... ; S1N1 выполняется оператор за оператором.

Конструкция

while true do

begin

SI; S2; SN

end

означает, что каждый процесс может выполняться неопределенное время. Конструкция типа begin end означает «пустой» оператор.

Решение, представленное в листинге 1, обеспечивает взаимное исключение в работе критических интервалов. Однако если бы часть программы PR1 была намного длиннее, чем программа PR2, или если бы процесс ПР1 был заблокирован в секции PR1, или если бы процессор для ПР2 был с большим быстродействием, то процесс ПР2 вскоре вынужден был бы ждать (и, может быть, чрезвычайно долго) входа в свою критическую секцию CS2, хотя процесс ПР1 и был бы вне CS1. То есть при таком решении один процесс вне своей критической секции может помешать вхождению другого в свою критическую секцию.

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

Пусть с каждым из процессов ПР1 и ПР2 будет связана переменная, по смыслу являющаяся переключателем, которая принимает значение true, когда процесс находится в своем критическом интервале, и false – в противном случае. Прежде чем войти в свой критический интервал, процесс проверяет значение переключателя другого процесса. Если это значение равно true, процессу не разрешается входить в свой критический интервал. В противном случае процесс «включает» свой собственный переключатель и входит в критический интервал. Этот алгоритм взаимного исключения представлен в листинге 2.

Листинг 2. Второй вариант попытки реализации взаимного исключения.

Var

перекл_1, перекл_2 : boolean;

Begin

перекл_1 :=false;

перекл_2 :=false;

ParBegin

While true do

Begin

while перекл_2 do

begin

Цикл_1;

end;

перекл_1:=true;

CS1: {Критическая секция процесса PR1}

перекл_1:=false;

PR1: {Оставшаяся часть процесса PR1}

End;

And

While true do

Begin

while перекл_1 do

begin

Цикл_1;

end;

перекл_2:=true;

CS2: {Критическая секция процесса PR2}

перекл_2:=false;

PR2: {Оставшаяся часть процесса PR2}

End;

ParEnd;

End.

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

Пусть процесс_1 находится в Цикле_1, в то время как процесс_2 находится в своем критическом участке. Процесс_2 заканчивает выполнять свой критический участок и устанавливает в переключателе_2 значение «ложь». Тем самым он освобождает процесс_1 от выполнения Цикла_1. Поскольку скорости произвольны, допустим, что процесс 2 работает гораздо быстрее, чем процесс_1. Настолько быстрее, что фактически, после того как процесс_1 обнаруживает, что в переключателе_2 стоит «ложь», но прежде чем он успеет установить значение «истина» в переключателе_1, процесс_2 пробегает свою оставшуюся часть и перескакивает через Цикл_2 (поскольку в переключателе_1 все еще стоит значение «ложь»). Оба процесса могут «безопасно» продолжать работу, установив в своих соответствующих переключателях значение «истина» и перейдя к выполнению своих критических участков. Предположив, что скорости произвольны, мы получили такую последовательность событий, при которой оба процесса получают возможность выполнять свои критические участки одновременно. Таким образом, проблема критического участка в примере не решена.

Чтобы решить задачу усилим в этом варианте взаимное исключение, так чтобы в процессе ПР1 проверка значения переменной перекл_2 выполняется после установки переменной перекл_1 в значение true (аналогично и для ПР2).

Листинг 3 Третий вариант попытки реализации взаимного исключения.

Var

перекл_1, перекл_2 : boolean;

Begin

перекл_1 :=false;

перекл_2 :=false;

ParBegin

PR1:

While true do

Begin

перекл_1:=true;

while перекл_2 do

begin

end;

CS1 {Критическая секция процесса PR1}

перекл_1:=false;

PR1 {Оставшаяся часть процесса PR1}

End;

And

PR2:

While true do

Begin

перекл_2:=true;

while перекл_1 do

begin

end;

CS2 {Критическая секция процесса PR2}

перекл_2:=false;

PR2 {Оставшаяся часть процесса PR2}

End;

ParEnd;

End.

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

Рассмотренные попытки решить проблему критических интервалов иллюстрируют некоторые тонкости, лежащие в основе этой проблемы.

Последний вариант решения задачи взаимного исключения, использующий только блокировку памяти, который мы рассмотрим, – это известный алгоритм, предложенный математиком Деккером.

Листинг 4. Алгоритм Деккера.

Label 1, 2;

Var

перекл_1, перекл_2 : boolean;

Очередь: Integer;

Begin

перекл_1 :=false;

перекл_2 :=false;

Очередь :=1

ParBegin

While true do

Begin

перекл_1:=true;

1: if перекл_2=true then

if Очередь =1 then goto 1

else

Begin

перекл_1 :=false;

while Очередь =2 do

begin

end;

End;

else

Begin

CS1 {Критическая секция процесса PR1}

Очередь:=2;

перекл_1 :=false;

End;

End;

And

While true do

Begin

перекл_2:=true;

2: if перекл_1=true then

if Очередь =2 then goto 2

else

Begin

перекл_2 :=false;

while Очередь =1 do

begin

end;

End;

else

Begin

CS2 {Критическая секция процесса PR1}

Очередь:=1;

перекл_2 :=false;

End;

End;

ParEnd;

End.

Алгоритм Деккера основан на использовании трех переменных: перекл_1, перекл_2 и Очередь. Пусть переменная пepeкл_l = true тогда, когда процесс ПР1 хочет войти в свой критический интервал (для ПР2 аналогично), а значение переменной Очередь указывает, чье сейчас право сделать попытку входа, при условии, что оба процесса хотят выполнить свои критические интервалы.

Если перекл_2 = true и пepeкл_1 = false, то выполняется критический интервал процесса ПР2 независимо от значения переменной Очередь. Аналогично для случая пepeкл_2 = false и перекл_1 = true.

Если же оба процесса хотят выполнить свои критические интервалы, то есть перекл_2 = true и перекл_1 = true, то выполняется критический интервал того процесса, на который указывало значение переменной Очередь, независимо от скоростей развития обоих процессов. Использование переменной Очередь совместно с перекл_1 и перекл_2 в алгоритме Деккера позволяет гарантированно решать проблему критических интервалов. Переменные перекл_1 и перекл_2 обеспечивают, что взаимное выполнение не может иметь места; переменная Очередь гарантирует от взаимного блокирования. Взаимное блокирование невозможно, так как переменная не изменяет своего значения во время выполнения программы принятия решения о том, кому же сейчас проходить свой критический интервал.

Тем не менее реализация критических интервалов на основе описанного алгоритма практически не используется из-за чрезмерной сложности, особенно в случаях, когда алгоритм Деккера обобщается с двух до N процессов.