- •Лекція №7 Паралельні обчислення: взаємовиключення і багатозадачність
- •7.1. Принципы параллельных вычислений
- •7.1.2. Взаимодействие процессов
- •7.1.3. Требования к взаимным исключениям
- •7.2. Взаимоисключения: программный подход
- •7.2.1. Алгоритм Деккера
- •Листинг 7.1. Алгоритм Деккера для двух процессов
- •7.2.2. Алгоритм Петерсона
- •Листинг 7.2. Алгоритм Петерсона для двух процессов
- •7.3. Взаимоисключения: аппаратная поддержка
- •7.3.2. Инструкция обмена
- •7.3.3. Свойства подхода, использующего машинные инструкции
7.3.2. Инструкция обмена
Выполнение команды Swap (обменять значения), обменивающей два значения, находящихся в памяти, можно проиллюстрировать следующей функцией:
Void Swap(int register, int memory);
{
int temp;
temp = memory;
memory = register;
register = temp;
}
Инструкция обменивает содержимое регистра и ячейки памяти. В процессе ее выполнения доступ к ячейке памяти для всех остальных процессов блокируется.
В листинге 7.4 показан протокол взаимных исключений, основанный на использовании описанной инструкции. Разделяемая переменная bolt инициализируется нулевым значением. У каждого процесса имеется локальная переменная key, инициализированная значением 1. В критический раздел может войти только один процесс, который обнаруживает, что значение переменной bolt равно 0. Этот процесс запрещает вход в критический раздел всем другим процессам путем установки значения bolt, равным 1. Выйдя из критического раздела процесс переустанавливает значение bolt, равным 0, тем самым позволяя другому процессу войти в критический раздел.
Листинг 7.4. Аппаратная проверка взаимных исключений. Инструкция обмена
const int n = /* Количество процессов */;
int bolt;
void P(int I)
{
int keyi;
while(true)
{
keyi = 1;
while(keyi != 0)
Swap(keyi, bolt);
/* Критический раздел */
Swap(keyi, bolt);
/* остальная часть кода */
}
}
void main()
{
bolt = 0;
parbegin(P(1),P(2),…,P(n));
}
Если bolt = 0, то в критическом разделе нет ни одного процесса. Если bolt = 1, то в критическом разделе находится ровно один процесс, а именно тот, переменная key которого имеет нулевое значение.
Заметим, что при использовании рассмотренного алгоритма всегда выполняется следующее соотношение:
Bolt + ∑I keyi = n
Примером приведенных инструкций может служить тот факт, что многие компьютеры, особенно разработанные с расчетом на несколько процессоров, имеют команду TSL RX,LOCK (Test and Set Lock – проверить и заблокировать), которая действует следующим образом. В регистр RX считывается содержимое слова памяти LOCK, а в ячейке памяти LOCK сохраняется некоторое ненулевое значение. Гарантируется, что операция считывания слова и сохранения неделима. Процессор, выполняющий команду TSL, блокирует шину памяти, чтобы остальные процессоры не могли обратиться к памяти.
Воспользуемся командой TSL. Пусть совместно используемая переменная Lock управляет доступом к разделяемой памяти. Если значение переменной Lock равно 0, любой процесс может изменить его на 1 и обратиться к разделяемой памяти, и затем изменить его обратно на 0, пользуясь обычной командой move.
Как использовать эту команду для выполнения взаимоисключения? Решение приведено в листинге 7.5, написанное на фиктивном ассемблере.
Листинг 7.5. Вход-выход из критической области с помощью команды TSL
Enter_Region:
TSL Register,Lock ; значение Lock копируется в регистр, значение ; Lock устанавливается равным 1
CMP Registr,#0 ; старое значение Lock сравнивается с 0
JNE Enter_Region ; если Lock<>0, блокировка уже была
; установлена, поэтому цикл завершается
RET ; возврат к вызывающей программе, процесс
; вошел в критическую область
Leave_Region:
MOVE Lock,#0 ; занесение 0 в переменную Lock
RET