Теорія операційної системи

:: Меню ::

Головна
Представлення даних в обчислювальних системах
Машинні мови
Завантаження програм
Управління оперативною пам'яттю
Сегментна і сторінкова віртуальна пам'ять
Комп'ютер і зовнішні події
Паралелізм з точки зору програміста
Реалізація багатозадачності на однопроцесорних комп'ютерах  
Зовнішні пристрої
Драйвери зовнішніх пристроїв
Файлові системи
Безпека
Огляд архітектури сучасних ОС

:: Друзі ::

Карта сайту
 

:: Статистика ::

 

 

 

 

 

Примітиви синхронізації

  Я чую крик в темноті
Напевно, це сигнал.
В. Бутусов

Поглянувши на приклади 7.2 і 7.4, уважний читач повинен відзначити, що використовувана конструкція підозріло схожа на роботу із зовнішніми пристроями в режимі опиту. Дійсно, опит прапором змінному в циклі хоча і забезпечує гарантію того, що взаємовиключає, але володіє всіма недоліками, які ми вказували для опиту зовнішнього пристрою.
g випадку виконання паралельних ниток на одному процесорі, даний метод має ще один недолік: поки одна з ниток займається опитом, жодна інша нитка не може виконуватися, бо процесор завантажений непродуктивною роботою.
Легко бачити, що в даному випадку використання переривань або якогось їх аналога проблеми не вирішить: в кращому разі, "переривання" викликатиметься не в тій нитці, в якій потрібно, зводячи завдання того, що взаємовиключає до попередньої, лише з вже новою змінною прапора, а в гіршому — приведе до виникнення ще однієї нитки. Завдання взаємодії між асинхронними нитками, таким чином, зводиться до вимоги того, щоб нитки в якісь моменти переставали бути асинхронними, синхронізовалісь.
Якщо у нас вже є примітив взаємного виключення, ми можемо вирішити завдання синхронізації, надавши ще один примітив, який дозволяє активному процесу зупинитися, чекаючи, поки змінна прапора не набуде "правильного" значення, і продовжити виконання після цього. При обробці переривань роль такого примітиву може виконувати команда зупинки процесора: у всіх сучасних процесорів переривання зупиняє "виконання" цієї команди, а повернення з обробника передає управління на наступну команду, таким чином виводячи процесор із сплячого достатку. У багатопроцесорній конфігурації можна ввести засіб, за допомогою якого один процесор може викликати переривання іншого, — і тоді кожен з процесорів системи зможе чекати іншого, переходячи в режим сну. При реалізації ж многопоточностп на одному процесорі (див. разд. Витісняюча багатозадачність) примітив засипання (блокування) нитки повинен надаватися модулем, відповідальним за перемикання потоків.
Втім, якщо операції над прапором, засипання потоку і його пробудження реалізовані різними примітивами, ми ризикуємо отримати нову проблему (приклад 7.5). Вона полягає в тому, що якщо сигнал, що будить, вчинить в проміжку між операторами testandset і pause ми його не отримаємо. В результаті операція pause приведе до засипання нашої нитки назавжди.

Приклад 7.5. Помилка втраченого пробудження (lost wake-up bug)

program пауза
var flag: Boolean;
procedure процесс1
var myflag: Boolean
while True do
begin
myflag := True;
testandset(myflag, flag);
if myflag then
(* Звернете увагу, що перевірка прапора *
* і засипання — це різні оператори! *)
pause;
крітічеськаясекция();
flag := False;
end
end;

Одне з рішень полягає в ускладненні примітиву pause: він повинен засинати, якщо і лише якщо сигнал ще не приходив. Ускладнення виходить значне: мало того, що перед засипанням треба перевіряти нетривіальну умову, необхідно ще передбачити якийсь спосіб скидання цієї умови, якщо ми передбачаємо багатократне використання нашого примітиву.
Якщо писати на асемблері або родинних йому мовах, можна піти і витонченішим дорогою (приклад 7.6). Підміна адреси повернення в обробнику переривання гарантує нам, що якщо переривання по установці прапора станеться в проміжку між мітками label і ok ми перейдемо на мітку label і, замість того, щоб заснути навіки, благополучно перевіримо прапор і увійдемо до критичної секції.

Приклад 7.6. Обхід помилки втраченого пробудження

.globl flag
flag: db 0
jmpbuf: dw 0
proc flag_interrupt
push eax
tst jmpbuf
bz setflagonly
; підміняємо адресу повернення
move eax, jmpbuf
move sp[Return_address_offset], eax setflagonly
move eax, 1
move flag, eax
pop eax
iret endp
proc process!
inove eax, setjmp
move jmpbuf, eax setjmp:
move eax, 1
lock xchg eax, flag
tst eax
bz ok
halt
ok
xor eax, eax move jmpbuf, eax
критична секція
xor eax, eax move flag, eax
endp

Елегантніший і прийнятніший для мов високого рівня дорога вирішення цієї проблеми полягає в тому, щоб об'єднати в атомарну операцію перевірку прапора і засипання. Нас з читачем можна привітати з винаходом двійкового семафора Дейкстри.

 

:: Реклама ::

 

:: Посилання ::


 

 

 


Copyright © Kivik, 2017