Взаимное исключение с активным ожиданием

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

Запрещение прерываний.

Самое простое решение заключается в запрещении всех прерываний при входе процесса в критическую область и разрешении прерываний при выходе из области. Если прерывания запрещены, то невозможно прерывание по таймеру. Поскольку процессор переключается с одного процесса на другой только по прерыванию. то отключение прерываний исключает передачу процессора другому процессу. Таким образом, запретив прерывания, процесс может спокойно считывать и сохранять совместно используемые данные, не опасаясь вмешательства другого процесса.
И все же было бы неразумно давать пользовательскому процессу возможность запрета прерываний. Представим, что процесс отключил все прерывания и в результате какого-либо сбоя не включил их обратно. Операционная система на этом моменте может закончить свое существование =)).
К тому же в многопроцессорной системе запрещение прерываний повлияет только на тот процессор, который выполнит инструкцию disable. Остальные процессоры продолжат работу и получат доступ к разделенным данным.

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

Переменные блокировки.

Теперь попробуем найти программное решение.

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

Строгое чередование

фкн -активное ожидание)))

На рисунке =

  1. целая переменная turn, изначально равная 0, отслеживает, чья очередь входить в критическую область.
  2. Вначале процесс 0 проверяет значение turn, считывает 0 и входит в критическую область(*см. примечание ниже)
  3. Процесс 1 также считывает значение turn, считывает 0 и после этого входит в цикл, непрерывно проверяя, когда значение turn будет равно 1.

*примечание:
-----------------------------------
на мой взгляд на картинке выше

опечатка

,чтобы задуманное выполнилось(Вначале процесс 0 проверяет значение turn, считывает 0 и входит в критическую область) необходимо в левом цикле (а) вместо

while (turn!=0) ; // неправильно! (по-моему)

написать =

while (turn!=1) ; // правильно (по-моему)

так как именно во втором случае процесс 0 при изначальном turn=0 сможет миновать цикл
-----------------------------------

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

Блокировка, использующая активное ожидание, называется спин-блокировкой.

Когда процесс 0 покидает критическую область, он изменяет значение turn на 1, позволяя процессу 1 попасть в критическую область.
Фактически этот метод требует, чтобы два процесса попадали в критическую область строго по очереди. Не один из них не может попасть в критическую секцию (например, послать файл на печать) два раза подряд.

Алгоритм Петерсона

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

Команда TSL


Рассмотрим решение, требующее аппаратного обеспечения
.
Многие компьютеры, особенно разработанные с расчетом на несколько процессоров, имеют команду
TSL RX.LOCK
(Test and Set Lock), которая действует следующим образом-
В регистр RX считывается содержимое слова памяти lock, а в ячейке памяти lock хранится некоторое ненулевое значение.
Гарантируется, что операция считывания слова и сохранения неделима - другой процесс не может обратиться к слову в памяти, пока команда не выполнена. Процессор, выполняющий команду TSL, блокирует шину памяти, чтобы остальные процессоры не могли обратиться к памяти.
Рассмотрим пример использования команды TSL для взаимного исключения.
фкн - команда TSL
Прежде чем попасть в критическую область, процесс вызывавает процедуру enter_region, которая выполняет активное ожидание вплоть до снятия блокировки, затем она устанавливает блокировку и возвращается.
При выходе из критической области процесс вызывает процедуру leave_region, помещающую ноль в переменную lock. Как и в остальных способах решения проблемы критической области, для корректной работы процесс должен вызвать эти процедуры своевремнно, в противном случае взаимное исключение не удастся.