Использование семафоров
Функция down_interruptible()
выполняет попытку захватить данный семафор. Если эта попытка неудачна, то задание переводится в состояние ожидания с флагом TASK_INTERRUPTIBLE
. Из материала главы 3 следует вспомнить, что такое состояние процесса означает, что задание может быть возвращено к выполнению с помощью сигнала и что такая возможность обычно очень ценная. Если сигнал приходит в тот момент, когда задание ожидает на освобождение семафора, то задание возвращается к выполнению, а функция down_interruptible()
возвращает значение -EINTR
. Альтернативой рассмотренной функции выступает функция down()
, которая переводит задание в состояние ожидания с флагом TASK_UNINTERRUPTIBLE
. В большинстве случаев это нежелательно, так как процесс, который ожидает на освобождение семафора, не будет отвечать на сигналы. Поэтому функция down_interruptible()
используется значительно более широко, чем функция down()
. Да, имена этих функций, конечно, далеки от идеала.
Функция down_trylock()
используется для неблокирующего захвата указанного семафора. Если семафор уже захвачен, то функция немедленно возвращает ненулевое значение. В случае успеха по захвату блокировки возвращается нулевое значение и захватывается блокировка.
Для освобождения захваченного семафора необходимо вызвать функцию up()
. Рассмотрим следующий пример.
/* объявление и описание семафора с именем mr_sem и
первоначальным значением счетчика, равным 1 */
static DECLARE_MUTEX(mr_sem);
...
if (down_interruptible(&mr_sem))
/* получен сигнал и семафор не захвачен */
/* критический участок ... */
/* освободить семафор */
up(&mr_sem);
Полный список функций работы с семафорами приведен в табл. 9.5.
Таблица 9.5. Список функций работы с семафорами
Функция | Описание |
---|---|
sema_init(struct semaphore*, int) |
Инициализация динамически созданного семафора и установка для него указанного значения счетчика использования |
init_MUTEX(struct semaphore*) |
Инициализация динамически созданного семафора и установка его счетчика использования в значение 1 |
init_MUTEX_LOCKED (struct semaphore*) |
Инициализация динамически созданного семафора и установка его счетчика использования в значение 0 (т.е. семафор изначально заблокирован) |
down_interruptible(struct semaphore *) |
Выполнить попытку захватить семафор и перейти в прерываемое состояние ожидания, если семафор находится в состоянии конфликта при захвате (contended) |
down(struct semaphore*) |
Выполнить попытку захватить семафор и перейти в непрерываемое состояние ожидания, если семафор находится в состоянии конфликта при захвате (contended) |
down_trylock(struct semaphore*) |
Выполнить попытку захватить семафор и немедленно возвратить ненулевое значение, если семафор находится в состоянии конфликта при захвате (contended) |
up(struct semaphore*) |
Освободить указанный семафор и возвратить к выполнению ожидающее задание, если такое есть |
Семафоры чтения-записи
Семафоры, так же как и спин-блокировки, могут быть типа чтения-записи. Ситуации, в которых предпочтительнее использовать семафоры чтения-записи такие же как и в случае использования спин-блокировок чтения-записи.
Семафоры чтения-записи представляются с помощью структуры struct rw_semaphore
, которая определена в файле <asm/rwsem.h>
. Статически определенный семафор чтения-записи может быть создан с помощью функции
static DECLARE_RWSEM(name);
где name
— это имя нового семафора.
Семафоры чтения-записи, которые создаются динамически, могут быть инициализированы с помощью следующей функции.
init_rwsem(struct rw_semaphore *sem);
Все семафоры чтения-записи являются взаимоисключающими (mutex), т.е. их счетчик использования равен единице. Любое количество потоков чтения может одновременно удерживать блокировку чтения, если при этом нет ни одного потока записи. И наоборот, только один поток записи может удерживать блокировку, захваченную на запись, если нет ни одного потока чтения. Все семафоры чтения-записи используют непрерываемое состояние ожидания, поэтому существует только одна версия функции down().
Рассмотрим следующий пример.
static DECLARE_RWSEM(mr_rwsem);
/* попытка захватить семафор для чтения */
down_read(&mr_rwsem);
/* критический участок (только чтение) ... */
/* освобождаем семафор */
up_read(&mr_rwsem);
/* ... * /
/* попытка захватить семафор на запись */
down_write(&mr_rwsem);
/* освобождаем семафор */
/* критический участок (чтение и запись) ... */
up write(&mr_rwsem);
Для семафоров есть реализации функций down_read_trylock()
и down_write_trylock()
. Каждая из них принимает один параметр — указатель на семафор чтения-записи. Обе функции возвращают ненулевое значение, если блокировка захвачена успешно, и нуль, если блокировка находится в состоянии конфликта. Следует быть внимательными — поведение этих функций противоположно поведению аналогичных функций для обычных семафоров, причем без всякой на то причины!
Семафоры чтения-записи имеют уникальную функцию, аналога которой нет для спин-блокировок чтения-записи. Это функция downgrade_writer()
, которая автоматически превращает блокировку, захваченную на запись, в блокировку, захваченную на чтение.
Семафоры чтения-записи, так же как и спин-блокировки аналогичного типа, должны использоваться, только если есть четкое разделение между участками кода, которые осуществляют чтение, и участками кода, которые осуществляют запись. Использование механизмов блокировок чтения-записи приводит к дополнительным затратам, поэтому их стоит использовать, только если код можно четко разделить на участки чтения и записи.
Сравнение спин-блокировок и семафоров
Понимание того, когда использовать спин-блокировки, а когда семафоры является важным для написания оптимального кода. Однако во многих случаях выбирать очень просто. В контексте прерывания могут использоваться только спин-блокировки, и только семафор может удерживаться процессом, который находится в состоянии ожидания. В табл. 9.6 показан обзор требований того, какой тип блокировок использовать.