Последняя полезная функция семафоров — это то, что они позволяют иметь любое количество потоков, которые одновременно удерживают семафор. В то время как спин-блокировки позволяют удерживать блокировку только одному заданию в любой момент времени, количество заданий, которым разрешено одновременно удерживать семафор, может быть задано при декларации семафора. Это значение называется счетчиком использования ( usage count ) или просто счетчиком ( count ). Наиболее часто встречается ситуация, когда разрешенное количество потоков, которые одновременно могут удерживать семафор, равно одному, как и для спин-блокировок. В таком случае счетчик использования равен единице и семафоры называются бинарными семафорами ( binary semaphore ) (потому что он может удерживаться только одним заданием или совсем никем не удерживаться) или взаимоисключающими блокировками ( mutex , мьютекс ) (потому что он гарантирует взаимоисключающий доступ — mutual exclusion). Кроме того, счетчику при инициализации может быть присвоено значение, большее единицы. В этом случае семафор называется счетным семафором ( counting semaphore , семафор-счетчик ), и он допускает количество потоков, которые одновременно удерживают блокировку, не большее чем значение счетчика использования. Семафоры-счетчики не используются для обеспечения взаимоисключающего доступа, так как они позволяют нескольким потокам выполнения одновременно находиться в критическом участке. Вместо этого они используются для установки лимитов в определенном коде. В ядре они используются мало. Если вы используете семафор, то, скорее всего, вы используете взаимоисключающую блокировку (семафор со счетчиком, равным единице).
Семафоры были формализованы Эдсгером Вайбом Дейкстрой [50] Доктор Дейкстра (1930–2002 г.) один из самых талантливых ученых за всю (конечно, не очень долгую) историю существования вычислительной техники как области науки. Его многочисленные труды включают работы но проектированию операционных систем и по теории алгоритмов, сюда же входит концепция семафоров. Он родился в городе Роттердам, Нидерланды, и преподавал в университете штата Техас в течение 15 лет. Тем не менее, он был бы не очень доволен большим количеством директив GOTO в ядре Linux.
(Edsger Wybe Dijkstra) в 1968 году как обобщенный механизм блокировок. Семафор поддерживает две атомарные операции P()
и V()
, название которых происходит от голландских слов Proben (тестировать) и Verhogen (выполнить инкремент). Позже эти операции начали называть down()
и up()
соответственно.
В операционной системе Linux они имеют такое же название. Операция down()
используется для того, чтобы захватить семафор путем уменьшения его счетчика на единицу. Если значение этого счетчика больше или равно нулю, то блокировка захватывается успешно и задание может входить в критический участок. Если значение счетчика меньше нуля, то задание помещается в очередь ожидания и процессор переходит к выполнению каких-либо других операций. Об использовании этой функции говорят в форме глагола— семафор опускается ( down ) для того, чтобы его захватить. Метод up()
используется для того, чтобы освободить семафор после завершения выполнения критического участка. Эту операцию называют поднятием ( upping ) семафора.
Последний метод используется для инкремента значения счетчика. Если очередь ожидания семафора не пуста, то одно из заданий этой очереди возвращается к выполнению и захватывает семафор.
Создание и инициализация семафоров
Реализация семафоров зависит от аппаратной платформы и определена в файле . Структура struct semaphore
представляет объекты типа семафор. Статическое определение семафоров выполняется следующим образом.
static DECLARE_SEMAPHORE_GENERIC(name, count);
где name
— имя переменной семафора, a count
— счетчик семафора. Более короткая запись для создания взаимоисключающей блокировки (mutex), которая используются наиболее часто, имеет следующий вид.
static DECLARE_MUTEX(name);
где name
— это снова имя переменной типа семафор. Чаще всего семафоры создаются динамически, как часть больших структур данных. В таком случае для инициализации семафора, который создается динамически и на который есть только непрямая ссылка через указатель, необходимо использовать функцию
sema_init(sem, count);
где sem
— это указатель, a count
— счетчик использования семафора. Аналогично для инициализации динамически создаваемой взаимоисключающей блокировки можно использовать функцию
Читать дальше