Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
173
Добавлен:
04.06.2015
Размер:
421.89 Кб
Скачать

Синхронизация на основе аппаратной поддержки атомарных операций

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

Рассмотрим одну из этих операций, традиционно используемых для синхронизации, - операцию TestAndSet, которая атомарно выполняет считывание и запоминание значения переменной, затем изменяет его на заданное значение, но в результате выдает первоначальное значение переменной.

Предположим, что в системе имеется аппаратная поддержка следующей атомарной операции:

boolean TestAndSet (boolean & target) {

boolean rv = target;

target = true;

return rv;

}

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

boolean lock = false;

Код i-го процесса будет иметь вид:

do {

while (TestAndSet (lock));

критическая секция

lock = false;

остальная часть кода

} while (1)

Значение переменной lock, равноеtrue, означает, что вход в критическую секцию заблокирован. Каждый процесс ждет, пока он не разблокируется, затем, в свою очередь, выполняет блокировку и входит в критическую секцию. При ее завершении процесс разблокирует критическую секцию присваиваниемlock значенияfalse.

Другое распространенное аппаратное решение для синхронизации – атомарная операция Swap, выполняющая перестановку значений двух переменных:

void Swap (Boolean * a, Boolean * b) {

Boolean temp = * a;

a = * b;

* b = temp;

}

Взаимное исключение по критическим секциям с помощью атомарной операции Swap реализуется следующим образом (приведен кодi-го процесса) :

/* общие данные */

boolean lock = false;

Boolean key = false;

/* код процесса i */

do {

key = true;

while (key) {

Swap (&lock, &key);

}

критическая секция

lock = false;

остальная часть кода

} while (1)

При данной реализации, условием ожидания процесса перед входом в критическую секцию является условия (key == true), которое фактически означает то же, что и в предыдущей реализации, - закрытое состояние блокировщика, т.е., то, что другой процесс находится в своей критической секции. Когда критическая секция освободится (освобождение осуществляется присваиваниемlock = false после завершения критической секции в исполнившем ее процессе), ее начнет исполнять текущий процесс.

Синхронизация на основе общих семафоров

Мы уже начали рассматривать семафоры Дейкстры как средство синхронизации в обзорной части курса. Здесь мы рассмотрим их более подробно в общем виде. Общий семафор (counting semaphore), по Э. Дейкстре, - этоцелая переменнаяS, над которой определены две атомарных семафорных операцииwait (S) иsignal (S) со следующей семантикой:

wait (S):

while (S <= 0) do no-op;

S--;

signal (S):

S++;

Фактически, если начальное значение общего семафора равно n (> 0), то это число задает количество процессов, которые могут беспрепятственно выполнить над семафором операциюwait.

Синхронизация по критическим секциям с помощью общего семафора осуществляется следующим образом:

/* общие данные */

semaphore mutex = 1;

do {

wait (mutex);

критическая секция

signal (mutex);

остальная часть кода

} while (1)

Соседние файлы в папке все лекции по ОС