futex(7) быстрая блокировка в пользовательском пространстве

ОБЗОР


#include <linux/futex.h>

ОПИСАНИЕ

Ядро Linux предоставляет фьютексы (futexes, «быстрые мьютексы (mutexes, взаимоисключающие блокировки) в пользовательском пространстве») в качестве строительного блока для быстрой блокировки в пользовательском пространстве и семафоров. Фьютексы очень просты и полезны для создания высокоуровневых абстракций блокировок, таких как мьютексы, условных переменных, блокировок чтения-записи, барьеров и семафоров.

Большинство программистов, фактически, не используют фьютексы напрямую, а полагаются на системные библиотеки, которые на них построены, такие как Native POSIX Thread Library (NPTL) (смотрите pthreads(7)).

Фьютекс отождествляется с участком памяти, который может быть общим для процессов или нитей. В этих процессах фьютекс может быть доступен по разным адресам. В своей основе фьютекс имеет семантику семафора; это счётчик, который можно увеличивать и уменьшать атомарно; процессы могут ждать пока значение не станет положительным.

Работа с фьютексом выполняется полностью в пользовательском пространстве при отсутствии конфликта (noncontended case). Ядро привлекается только для разрешения конфликтов. Так как все проектные решения стремятся к отсутствию конфликтов, фьютексы также оптимизированы под эту ситуацию.

В своей основе фьютекс — это целое число, которое изменяется только атомарными инструкциями ассемблера. Это целое число размером 4 байта на всех платформах. Процессы могут совместно использовать это число посредством mmap(2), сегментов общей памяти или общего пространства памяти (share memory space, в этом случае приложение, обычно, называют многонитивым).

Поведение

Любое действие с фьютексом начинается в пользовательском пространстве, но может потребовать обращения к ядру через системный вызов futex(2).

Для «установки» фьютекса выполняются соответствующие ассемблерные инструкции, которые заставляют ЦП машины атомарно увеличить целое число. Далее проверяется, было ли действительно изменено значение с 0 на 1, то есть не было ожидающих и операция выполнена. Это бесконфликтный вариант, выполняется быстро и должен возникать чаще всего.

При возникновении конфликта, атомарное увеличение изменяет счётчик с -1 (или другого отрицательного числа). Это означает, что есть ожидающие. В пользовательском пространстве теперь нужно присвоить счётчику 1 и дать команду ядру пробудить всех ожидающих с помощью операции FUTEX_WAKE.

Ожидание фьютекса, его «сброс», является обратной операцией. Происходит атомарное уменьшение счётчика и проверка того, стал ли он равен 0, то есть операция выполнена и фьютекс был неконфликтным. Во всех других случаях, процесс должен присвоить счётчику -1 и запросить ядро об ожидании другого процесса, устанавливающего фьютекс. Это выполняется с помощью операции FUTEX_WAIT.

В системном вызове futex(2) можно указать время ожидания, то есть как долго ядро должно ждать установку фьютекса, В этом случае семантика более сложна и программисту нужно обратиться к futex(2). Это тоже самое что и ожидания асинхронного фьютекса.

ВЕРСИИ

Впервые поддержка фьютексов появилась в Linux 2.5.7, но с другой семантикой. Текущая семантика используется в Linux с версии 2.5.40.

ЗАМЕЧАНИЯ

Ещё раз повторим: в чистом виде фьютексы не являются лёгкой в использовании абстракцией для конечных пользователей. Использующие их программисты должны иметь хороший запас знаний об ассемблере и уметь читать исходный код библиотеки фьютексов для пользовательского пространства, указанной далее.

В этой справочной странице показано самое распространённое использование примитивов futex(2), которое ни в коем случае не единственное.