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 показан обзор требований того, какой тип блокировок использовать.
Таблица 9.6. Что следует использовать: семафоры или спин-блокировки
Требование | Рекомендуемый тип блокировки |
---|---|
Блокировка с малыми накладными затратами (low overhead) | Спин-блокировки более предпочтительны |
Малое время удержания блокировки | Спин-блокировки более предпочтительны |
Длительное время удержания блокировки | Семафоры более предпочтительны |
Необходимо использовать блокировку в контексте прерывания | Необходима спин-блокировка |
Необходимо переходить в состояние ожидания (steep) при захваченной блокировке | Необходимо использовать семафоры |
Условные переменные
Условные переменные (conditional variable, completion variable) — простое средство синхронизации между двумя заданиями, которые работают в режиме ядра, когда необходимо, чтобы одно задание послало сигнал другому о том, что произошло некоторое событие. При этом одно задание ожидает vfork()
условная переменная используется для возврата к выполнению родительского процесса при завершении порожденного.
Условные переменные представляются с помощью структуры struct completion
, которая определена в файле
.