Последняя полезная функция семафоров — это то, что они позволяют иметь любое количество потоков, которые одновременно удерживают семафор. В то время как спин-блокировки позволяют удерживать блокировку только одному заданию в любой момент времени, количество заданий, которым разрешено одновременно удерживать семафор, может быть задано при декларации семафора. Это значение называется
Семафоры были формализованы Эдсгером Вайбом Дейкстрой[50] (Edsger Wybe Dijkstra) в 1968 году как обобщенный механизм блокировок. Семафор поддерживает две атомарные операции P()
и V()
, название которых происходит от голландских слов down()
и up()
соответственно.
В операционной системе Linux они имеют такое же название. Операция down()
используется для того, чтобы захватить семафор путем уменьшения его счетчика на единицу. Если значение этого счетчика больше или равно нулю, то блокировка захватывается успешно и задание может входить в критический участок. Если значение счетчика меньше нуля, то задание помещается в очередь ожидания и процессор переходит к выполнению каких-либо других операций. Об использовании этой функции говорят в форме глагола— семафор up()
используется для того, чтобы освободить семафор после завершения выполнения критического участка. Эту операцию называют
Последний метод используется для инкремента значения счетчика. Если очередь ожидания семафора не пуста, то одно из заданий этой очереди возвращается к выполнению и захватывает семафор.
Создание и инициализация семафоров
Реализация семафоров зависит от аппаратной платформы и определена в файле
. Структура struct semaphore
представляет объекты типа семафор. Статическое определение семафоров выполняется следующим образом.
static DECLARE_SEMAPHORE_GENERIC(name, count);
где name
— имя переменной семафора, a count
— счетчик семафора. Более короткая запись для создания взаимоисключающей блокировки (mutex), которая используются наиболее часто, имеет следующий вид.
static DECLARE_MUTEX(name);
где name
— это снова имя переменной типа семафор. Чаще всего семафоры создаются динамически, как часть больших структур данных. В таком случае для инициализации семафора, который создается динамически и на который есть только непрямая ссылка через указатель, необходимо использовать функцию
sema_init(sem, count);
где sem
— это указатель, a count
— счетчик использования семафора. Аналогично для инициализации динамически создаваемой взаимоисключающей блокировки можно использовать функцию
init_MUTEX(sem);
Неизвестно, почему слово "mutex" в имени функции init_MUTEX()
выделено большими буквами и почему слово "init" идет перед ним, в то время как имя функции sema_init()
таких особенностей не имеет. Тем не менее ясно, что это выглядит не логично, и я приношу свои извинения за это несоответствие. Надеюсь, что после прочтения главы 7 ни у кого уже не будет вызывать удивление то, какие имена придумывают символам ядра.
Использование семафоров