Определяя более строго, семафор — это специальная переменная, для которой разрешены только две операции, формально именуемые ожиданием или приостановкой (wait) и оповещением (signal). Поскольку в программировании Linux у приостановки и оповещения уже есть специальные значения, мы будем применять оригинальное обозначение:
P
(переменная-семафор) для приостановки (wait);
V
(переменная-семафор) для оповещения (signal).
Эти буквы взяты из голландских слов для приостановки (
Описание семафора
Простейший семафор — это переменная, способная принимать только значения 0 и 1,
Определения операций P
и V
удивительно просты. Предположим, что у вас есть переменная-семафор sv
. В этом случае обе операции определяются так, как представлено в табл. 14.1.
Операция | Описание |
---|---|
Р(sv) | Если sv больше нуля, она уменьшается на единицу. Если sv равна 0, выполнение данного процесса приостанавливается |
V(sv) | Если какой-то другой процесс был приостановлен в ожидании семафора sv , переменная заставляет его возобновить выполнение. Если ни один процесс не приостановлен в ожидании семафора sv , значение переменной увеличивается на единицу |
Другой способ описания семафора — считать, что переменная sv
, равная true
, когда доступна критическая секция, уменьшается на единицу с помощью P(sv)
и становится равна false
, когда критическая секция занята, и увеличивается на единицу операцией V(sv)
, когда критическая секция снова доступна. Имейте в виду, что обычная переменная, которую вы уменьшаете и увеличиваете на единицу, не годится, т.к. в языках С, С++, C# или практически в любом традиционном языке программирования у вас нет возможности сформировать единую атомарную операцию, проверяющую, равна ли переменная true
, и если это так, изменяющую ее значение на false
. Именно эта функциональная возможность делает операции с семафором особенными.
Теоретический пример
С помощью простого теоретического примера можно посмотреть, как действует семафор. Предположим, что у вас есть два процесса: proc1 и proc2, оба нуждающиеся в некоторый момент выполнения в монопольном доступе к базе данных. Вы определяете один бинарный семафор sv
, который стартует со значением 1 и доступен обоим процессам. Далее обоим процессам нужно выполнить одну и ту же обработку для доступа к критической секции программного кода; эти два процесса могут быть двумя разными выполняющимися экземплярами одной и той же программы.
Оба процесса совместно используют переменную-семафор sv
. Как только один процесс выполнил операцию P(sv)
, он получил семафор и может войти в критическую секцию программы. Второму процессу вход в критическую секцию запрещен, т.к., когда он попытается выполнить операцию P(sv)
, он вынужден будет ждать до тех пор, пока первый процесс не покинет критическую секцию и не выполнит операцию V(sv)
, освобождающую семафор.
Требуемый псевдокод у обоих процессов идентичен:
semaphore sv = 1;
loop forever {
P(sv);
critical code section;
V(sv);
noncritical code section;
}
Код на удивление прост, потому что определение операций P
и V
наделяет их большими функциональными возможностями.
Рис. 14.1
На рис. 14.1 показана схема действующих операций P
и V
, напоминающих ворота в критических секциях программного кода.
Реализация семафоров в Linux