Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Операционные системы_лекции.doc
Скачиваний:
47
Добавлен:
28.09.2019
Размер:
1.71 Mб
Скачать

3.3.4. Применение специальных операций типа "проверка–установка"

Причиной возникновения проблем при реализации взаимного исключения посредством блокировки памяти является реализация механизма двумя командами "проверить флаг" и "становить флаг". Во время исполнения этой пары операций доступ к флагам имеют все процессы, конкурирующие за критический ресурс. Попытка решить эту проблему аппаратными средствами была сделана ещё в знаменитых машинах IBM 360 и IBM 370. В них появилась единая неделима команда TS (Test and Set) проверки и установки переменных. Т.к. эта команда неделима, то на время её использования одним процессом проверяемая переменная становится недоступна другим процессам.

Команда TS имеет формат:

TS (операнд_1, операнд_2).

В процессе её выполнения в операнд_1 копируется значение из операнд_2, а операнд_2 получает значение единица.

Для реализации взаимного исключения, как и в случае блокировки памяти, создаются два флага перекл_1 и перекл_2, а также переменная common. Все три переменные являются общими для конкурирующих за критический ресурс процессов. При запуске процессов Р1 и Р2 переменная common получает значение нуль. Перед вхождением в свои критические секции процессы Р1 и Р2 ставят свои флаги в состояние единица и выполняют команду TS:

  • процесс Р1: перекл_1:=1; TS(перекл_1,common);

  • процесс Р2: перекл_2:=1; TS(перекл_2,common).

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

В архитектуре IA32 команда TS получила своё развитие, в результате которого появились три команды BTC, BTS и BTR.

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

3.3.5.Семафоры и их применение

Одной из причин низкой эффективности рассмотренных в 2.3.3 и 2.3.4 методов реализации взаимных исключений является наличие двух и более переменных, анализируемых и устанавливаемых двумя раздельными или одной специальной командой. Более перспективным способом представляется использование для анализа возможности вхождения процесса в критическую секцию одной переменной и возможности запрета прерываний во время исполнения критической секции процесса.

Эта идея реализована в семафорных примитивах Дейкстры. Семафором называется переменная специального типа, которая доступна параллельным процессам. Для управления семафором S процессы могут использовать только две операции: "открыть" V(S) и "закрыть" P(S).

При старте процессов должна выполняться инициализация семафора, т.е. задание ему начального значения. Инициализация семафора осуществляется операцией InitSem(Имя_семафора, Начальное_состояние).

Семафор (S) является дополнительным критическим ресурсом, который указывается в качестве параметра операции, а операции P(S) и V(S) являются примитивами. Они неделимы и взаимно исключают друг друга.

Механизм семафора включается по запросу любого процесса из числа параллельных. Схема работы механизма семафора предусматривает проверку состояния ресурса, с которым связан семафор S, и в зависимости от состояния семафора допуск процесса к ресурсу или отказ в доступе к нему. При отказе в доступе процесс переводится в режим пассивного ожидания и, следовательно, не потребляет процессорное время. Разумеется, существует очередь ожидающих процессов. Средства обслуживания очереди входят в механизм семафорного процесса и реализуются супервизором операционной системы. Вследствие взаимного исключения примитивов P(S) и V(S) попытка одновременного выполнения примитивов несколькими разными процессами приведёт к тому, что только один из них успешно выполнит примитив, т.е. будет выполнено взаимное исключение процессов.

В настоящее время существует достаточно много семафорных механизмов, описанных в [5] и различающихся следующими параметрами:

  • начальное значение семафора;

  • диапазон допустимых значений семафора;

  • логика действий семафорных операций;

  • количество семафоров, доступных примитивам для обработки.

Различают семафоры с разным числом состояний. Существуют двоичные семафоры, имеющие два состояния: "открыт" и "закрыт". Такие семафоры называются мьютексами. Существуют семафоры, имеющие N состояний. Среди них различают семафоры столько положительными значениями и семафоры, допускающие отрицательные значения. В таких семафорах отрицательные значения указывают длину очереди процессов, ожидающих критический ресурс.

Два варианта реализации семафора показаны на рис. 3.4. В обоих случаях описания примитивов можно оформит в виде процедур или процедур-функций. Примитив P(S) вызывается процессом перед вхождением в критическую секцию. Примитив V(S) вызывается после выполнения критической секции.

В варианте рис. 3.4,а примитив P(S) сразу уменьшает значение семафора на единицу и, если новое значение семафора меньше нуля операцией WAIT(S) останавливает процесс и помещает его в очередь ожидания к семафору S. В варианте рис. 3.4,б сначала проверяется значение семафора и, если оно не меньше единицы, то значение семафора уменьшается на единицу. В противном случае операцией WAIT(S) процесс останавливается и помещается в очередь ожидания к семафору.

P(S): S:=S-1; If S<0 Then WAIT(S)

V(S): If S<0 Then RELEASE(S); S:=S+1

а)

P(S): If S>=1 Then S:=S-1 Else WAIT(S)

V(S): If S<0 Then RELEASE(S); S:=S+1

б)

Рис. 3.4. Неудачный (а) и удачный (б) варианты реализации примитивов P(S) и V(S)

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

Применение обоих вариантов реализации семафоров можно проиллюстрировать программным кодом, показанным на рис. 3.5.

Var S: Semafor

Begin

InitSem(S,1)

{описание переменной S типа Semafor}

{начало программного кода}

{инициализация семафора S значением единица}

Parbegin

{начало описания параллельных процессов}

while true do

{организация бесконечного процесса Р1 }

begin

P(S)

CS1

V(S)

end

{начало описания тела процесса Р1}

{вызов примитива P(S)}

{выполнение критической секции процесса Р1}

{вызов примитива V(S)}

{конец описания тела процесса Р1}

AND

{разделитель описаний процессов }

while true do

{организация бесконечного процесса Р2 }

begin

P(S)

CS1

V(S)

end

{начало описания тела процесса Р2}

{вызов примитива P(S)}

{выполнение критической секции процесса Р2}

{вызов примитива V(S)}

{конец описания тела процесса Р2}

Parend

{конец описания параллельных процессов}

End

{конец программного кода}

Рис. 3.5. Пример применения семафора

Один и тот же программный код (рис. 3.5) при использовании разных вариантов реализации семафора даст разный результат. Вариант реализации, показанный на рис. 3.4,б, будет надёжно работать, тогда как вариант рис. 3.4,а приведёт к ситуации, в которой заблокированы оба процесса (рис. 3.6.).

Рис. 3.6. Возникновение ситуации блокирования обоих процессов

Эта ситуация может возникнуть следующим образом. При запуске процессов Р1 и Р2 в семафор записана (+1). Оба процесса находятся в состоянии готовности. В момент времени t1 примитив P(S), вызванный процессом Р1 сбросит семафор в нуль. Во время выполнении критической секции CS1 процесса Р1 значение семафора равно нулю.

Однако в момент t2 примитив P(S), вызванный процессом Р2 запишет в семафор значение (-1), а сам процесс Р2 будет остановлен. В момент t3 примитив V(S) восстановит в семафоре значение ноль и переведет процесс Р2 в состояние готовности. Процесс Р1 останется в состоянии готовности.

Если Р2 попытается войти в свою критическую секцию, то это ему не удастся. В момент времени t4 в семафоре будет восстановлено значение (-1), процес Р2 будет остановлен и поставлен в очередь к семафору. Дальнейшая попытка процесса Р1 войти в свою критическую секцию будет также неудачна, процесс Р1 будет остановлен, и возникнет ситуация блокирования обоих процессов, из которой выхода нет.

Реализация неделимости примитивов P(S) и V(S) в однопроцессорной системе достигается простым отключением прерываний в критических секциях процессов и их последующим включением при выходе из неё. В многопроцессорных системах этот вариант не проходит, т.к. не запрещает одновременный запрос критического ресурса со стороны множества процессоров. Поэтому механизм семафоров несколько усложняется и дополняется аппаратной поддержкой взаимного исключения доступа для разных процессоров. Это может быть достигнуто, например, использованием неделимых команд "проверка – установка" TS, которые описаны в 3.3.4.

Некоторые особенности имеет применение двоичных семафоров (мьютексов). Они имеют два состояния "отмечен" (открыт) и "не отмечен" (закрыт). Мьютексы предназначены для организации взаимного исключения для потоков выполнения (задач) одного или нескольких процессов.

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

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

  • CreateMutex() – создание мьютекса;

  • OpenMutex() – открытие мьютекса;

  • WaitForSingleObject() – ожидание события от одного объекта;

  • WaitForMultipleObject() – ожидание события от множества объектов;

  • ReleaseMutex() – освобождение мьютексм.