Двійкові семафори Дейкстри

Зовсім іншим чином підійшов до проблеми взаємного виключення великий голландський учений Е.Дейкстра (E.Dijkstra, 1966). Він запропонував використати новий вид програмних об'єктів –

семафори. Тут ми розглянемо їх найпростіший варіант - двійкові семафори, вони ж мьютекс (mutex, від слів MUTual EXclusion - взаємне виключення).

Двійковим семафором називається змінна S, яка може приймати значення 0 і 1 і для якої визначені тільки дві операції.

· P (S) - операція заняття (закриття) семафора. Вона чекає, поки значення S стане рівним 1, і, як тільки це станеться, присвоює S значення 0 і завершує своє виконання. Дуже важливо: операція P за визначенням неподільна, тобто між перевіркою та присвоюванням не може вклинитися інший процес, який би змінив значення S.

· V (S) - операція звільнення (відкриття) семафора. Вона просто привласнює S значення 0.

Чим змінна-семафор відрізняється від звичайної булевої змінної? Тим, що для неї неприпустимі ніякі інші операції, крім P і V. Не можна написати в програмі S: = 1 або if (S) then ... , Якщо S визначена як семафор.

Чим операція P відрізняється від варіанту з перевіркою і присвоюванням, який ми вище визнали незадовільним? Неподільністю. Але це «за визначенням», а як на практиці досягти цієї неподільності? Це окремий, цілком вирішуване питання.

Заслуга Дейкстри якраз у тому, що він розділив проблему взаємного виключення на дві незалежні проблеми різних рівнів:

· На рівні реалізації: як забезпечити роботу семафорів відповідно до їх визначенням;

· На рівні взаємодії процесів: як написати коректно працюючу програму, якщо в розпорядженні програміста є семафори.

Вирішувати ці два завдання окремо легше, ніж обидві разом, при цьому вирішувати їх зазвичай повинні різні люди: першу - розробники ОС, а другу - розробники прикладної програми.

Розглянемо спочатку реалізацію. Очевидно, функції P і V зручніше і надійніше один раз реалізувати в ОС, ніж кожен раз по-новому - в прикладних програмах. (Назви цих функцій можуть в конкретних системах бути й іншими, більш виразними.)

Системна функція P (S) повинна перевірити, чи вільний семафор S. Якщо вільний (S = 1), то система займає його (S: = 0) і на цьому функція завершується. Якщо ж семафор зайнятий, то система блокує процес, що викликав функцію P, і запам'ятовує, що цей процес блокований по очікуванню звільнення семафора S. Таким чином, при реалізації семафорів вдається уникнути активного очікування.

Неподільність операції забезпечується тим, що під час виконання системою функції P перемикання процесів заборонено. В крайньому випадку, ОС має можливість для цього на короткий час заборонити переривання.

Системна функція V (S) - це, звичайно, не просто присвоювання S: = 1. Крім цього, система повинна перевірити, чи немає серед сплячих процесів такого, який очікує звільнення семафора S. Якщо такий процес знайдеться, система розблокує його, а змінна S в цьому випадку зберігає значення 0 (семафор знову зайнятий, тепер уже іншим процесом).

Чи може статися так, що кілька сплячих процесів чекають звільнення одного і того ж семафора? Так, так цілком може бути. Який з цих процесів має бути розбуджений системою? З точки зору коректності роботи і відповідності визначень функцій P і V - кожної, але тільки один. З точки зору

ефективності роботи - ймовірно, треба розбудити найпріоритетніший процес, а в разі рівності пріоритетів ... ну, мабуть, той, який спить довше.

Тепер, коли ми розібралися з реалізацією семафорів, можна про неї забути [9] і пам'ятати тільки, що семафори існують і можуть бути використані при необхідності.

Розглянемо тепер другу половину завдання - використання семафорів для управління взаємодією процесів. Як можна реалізувати коректну роботу процесів з критичними секціями, якщо використовувати двійковий семафор? Та дуже просто.

  Процесс A:   Процесс B:
. . . P(S); (критическая секция A) V(S); . . . . . . P(S); (критическая секция B) V(S); . . .  

І все. Складності пішли в реалізацію семафорів. Треба тільки простежити, щоб до початку роботи процесів семафор S був відкритий.