FUTEX(2)

ИМЯ
futex - системный вызов быстрых блокировок из пространства пользователя

ОБЗОР

#include <linux/futex.h>
#include <sys/time.h>

int futex(int *uaddr, int op, int val, const struct timespec *timeout,
          int *uaddr2, int val3);

ОПИСАНИЕ

Системный вызов futex() предоставляет программам метод для ожидания изменения значения по указанному адресу памяти и метод для пробуждения всех ожидающих на определённом адресе (хотя адреса для одного и того же участка памяти в разных процессах могут быть не идентичны, ядро отображает их внутренне так, что один участок памяти, отображённый в разные места, будет соответствовать одним вызовам futex()). Обычно этот системный вызов используется для реализации блокировок в общей памяти, как это описано в futex(7).

Когда операция futex(7) оканчивается без завершения спора в пространстве пользователя, должен быть сделан вызов к ядру для выноса решения. Решением может быть как усыпление вызывающего процесса, так и наоборот — пробуждение ожидающего процесса.

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

Аргумент uaddr должен указывать на выровненное целое, хранящее счетчик. Тип операции для исполнения указывает в параметре op, а её значение в val.

В данный момент определены пять операций:

FUTEX_WAIT
    Эта операция атомарно проверяет, что по адресу uaddr futex все ещё содержится значение val, и засыпает в ожидании FUTEX_WAKE на этом адресе futex. Если аргумент timeout не равен NULL, то его содержимое описывает максимальную задержку ожидания, иначе это время бесконечно. Аргументы uaddr2 и val3 игнорируются. По futex(7) этот вызов исполняется, если уменьшение счетчика даёт отрицательное значение (указывая на наличие спора), или будет спать, пока другой процесс не освободит futex и не выполнит операцию FUTEX_WAKE.

FUTEX_WAKE
    Эта операция пробуждает не больше val процессов, ожидающих на этом адресе futex (т.е. внутри FUTEX_WAIT). Аргументы timeout, uaddr2 и val3 игнорируются. По futex(7) этот вызов исполняется, если увеличение счетчика показало, что есть ожидающие, как только значение futex стало равным 1 (показывает, что есть доступные ожидающие).

FUTEX_FD (существует до Linux 2.6.25 включительно)
    Для поддержки асинхронных пробуждений эта операция связывает дескриптор файла с futex. Если другой процесс выполняет FUTEX_WAKE, то процесс будет получать номер сигнала, переданный в val. Вызывающий процесс должен закрыть возвращаемый дескриптор файла после использования. Аргументы timeout, uaddr2 и val3 игнорируются. Для предотвращения состязательности, вызывающий после возврата при FUTEX_FD должен проверять, не был ли futex увеличен. Так как по своей природе операция FUTEX_FD приводит к состязательности, она была удалена из Linux, начиная с версии 2.6.26.

FUTEX_REQUEUE (начиная с Linux 2.5.70)
    Эта операция была введена для того, чтобы избежать эффекта "громыхающего стада (thundering herd)", когда используется FUTEX_WAKE и все процессы просыпаются, чтобы захватить другой futex. Этот вызов пробуждает до val процессов и повторно ставит в очередь остальных ожидающих futex по адресу uaddr2. Аргументы timeout и val3 игнорируются.

FUTEX_CMP_REQUEUE (начиная с Linux 2.6.7)
    Чтобы избежать состязательности при FUTEX_REQUEUE, была введена FUTEX_CMP_REQUEUE. Она похожа на FUTEX_REQUEUE, но сначала проверяет, что расположенное по адресу uaddr значение всё ещё равно val3. Если нет, то операция завершается с ошибкой EAGAIN. Аргумент timeout игнорируется.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

В зависимости от типа используемой операции при успешном выполнении возвращаемое значение может иметь разный смысл.

FUTEX_WAIT
    Если процесс пробуждается по FUTEX_WAKE, то возвращается 0. В случае истечения срока таймера возвращается с ошибкой ETIMEDOUT. Если futex не был эквивалентен ожидаемому значению, то операция возвращается с ошибкой EWOULDBLOCK. При сигналах (см. signal(7)) или других случайных пробуждениях операция FUTEX_WAIT завершается с ошибкой EINTR.

FUTEX_WAKE
    Возвращается количество разбуженных процессов.

FUTEX_FD
    Возвращается новый файловый дескриптор, связанный с futex.

FUTEX_REQUEUE
    Возвращается количество разбуженных процессов.

FUTEX_CMP_REQUEUE
    Возвращается количество разбуженных процессов.

В случае ошибки все операции возвращают -1, а errno устанавливается в соответствующее значение.

ОШИБКИ

EACCES
    Нет доступа на чтение памяти futex.

EAGAIN
    При FUTEX_CMP_REQUEUE обнаружено не то значение futex. (Вероятно, это указывает на состязательность; используйте теперь безопасный FUTEX_WAKE.)

EFAULT
    Ошибка при получении информации timeout из пользовательского пространства.

EINVAL
    Неопределённая операция или ошибка выравнивания страницы.

ENFILE
    Достигнуто максимальное количество открытых файлов в системе.

ENOSYS
    В op задана неверная операция.

ВЕРСИИ

Начальная поддержка futex была встроена в Linux 2.5.7, но с другой семантикой, отличающейся от описанной выше. Семантика системного вызова с четырьмя аргументами, описанная в этой странице, появилась в Linux 2.5.40. В Linux 2.5.70 был добавлен ещё один аргумент. В Linux 2.6.7 был добавлен шестой аргумент — захламлено, особенно для архитектуре s390.

СООТВЕТСТВИЕ СТАНДАРТАМ
Данный вызов есть только в Linux.

ЗАМЕЧАНИЯ

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

СМОТРИТЕ ТАКЖЕ
futex(7)

Fuss, Futexes and Furwocks: Fast Userlevel Locking in Linux (доклад на симпозиума по Linux в Оттаве в 2002 году), доступно как
http://kernel.org/doc/ols/2002/ols2002-pages-479-495.pdf

Пример библиотеки futex, futex-*.tar.bz2, доступен на
ftp://ftp.nl.kernel.org/pub/linux/kernel/people/rusty/.