Апаратна підтримка взаємовиключень
Наявність апаратної підтримки взаємовиключень дозволяє спростити алгоритми і підвищити їх ефективність так само, як і в інших галузях програмування. Ми вже зверталися до загальноприйнятого hardware для вирішення задач реалізації взаємовиключень, коли говорили про використання механізму заборони/дозволення переривань.
Багато обчислювальних систем також мають спеціальні команди процесора, які дозволяють перевірити і змінити значення машинного слова або поміняти місцями значення двох машинних слів у пам'яті, виконуючи ці дії як атомарні операції. Давайте обговоримо, як концепції таких команд можна використовувати для реалізації взаємовиключень.
Команда Test-and-Set (Перевірити та присвоїти 1)
Про виконання команди Test-and-Set, що здійснює перевірку значення логічної змінної з одночасною установкою її значення в 1, можна думати, як про виконання функції
int Test_and_Set (int *target)
int tmp = * target; *target = 1; return tmp;
З використанням цієї атомарної команди ми можемо модифікувати наш алгоритм для змінної-замку, так щоб він забезпечував взаємовиключення
shared int lock = 0;
while (some condition)
while(Test_and_Set(&lock));
Critical section
lock = 0;
Remainder section
Команда Swap (Обмінювати значення)
Виконання команди Swap, яка обмінює два значення, що знаходяться в пам'яті, можна проілюструвати наступною функцією
void Swap (int *a, int *b)
int tmp = * a; *a = *b; * b = tmp;
Застосовуючи атомарну команду Swap ми можемо реалізувати попередній алгоритм, ввівши додаткову логічну змінну keyлокальну для кожного процесу:
shared int lock = 0; int key;
while (some condition)
key = 1; do Swap(&lock,&key); while (key);
Critical section
lock = 0;
Remainder section
Глава 6. Механізми синхронізації
Розглянуті наприкінці попередньої глави алгоритми хоч і є коректними, але досить громіздкі і не мають елегантності. Більш того, процедура очікування входу в критичний ділянку включає досить тривале обертання процесу в порожньому циклі, вхолосту пожираючи дорогоцінний час процесора. Існують інші серйозні недоліки в алгоритмів, побудованих засобами звичайних мов програмування. Припустимо, що в обчислювальній системі знаходяться два взаємодіючі процеси: один з них -H - з високим пріоритетом, інший -L - з низьким пріоритетом. Нехай планувальник влаштований так, що процес із високим пріоритетом витісняє низькопріоритетний процес щоразу, коли він готовий до виконання, і займає процесор на весь час свого CPU burst (якщо не з'явиться процес із ще більшим пріоритетом). Тоді у разі, коли процесL знаходиться у своїй критичній секції, а процесH, отримавши процесор, підійшов до входу в критичну область, ми отримуємо тупикову ситуацію. ПроцесH не може увійти в критичну область, перебуваючи в циклі, а процесL не отримує управління, щоб залишити критичний ділянку.
Для того, щоб усунути виникнення подібних проблем, були розроблені різні механізми синхронізації вищого рівня: семафори, монітори та повідомлення, розгляду яких і присвячений цей розділ.
Семафори
Одним із перших механізмів, запропонованих для синхронізаціїповедінки процесів стали семафорами, концепцію яких описав Дейкстра (Dijkstra) в 1965 році.
Концепція семафорів
Семафор являє собою цілу змінну, що набуває невід'ємних значень, доступ будь-якого процесу до якої, за винятком моменту її ініціалізації, може здійснюватися тільки через дві атомарні операції:P (від датського слова proberen - перевіряти ) іV (від verhogen - збільшувати). Класичне визначення цих операцій виглядає так:
| P(S): | поки S |