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);
Замечание:В glibc нет обёрточной функции для данного системного вызова; смотрите ЗАМЕЧАНИЯ.

ОПИСАНИЕ

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

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

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

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

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

FUTEX_WAIT
    Эта операция атомарно проверяет, что адрес futex uaddr по прежнему содержит значение val и засыпает, ожидая FUTEX_WAKE для этого адреса futex. Если аргумент timeout не равен NULL, то его значение задаёт длительность ожидания (Этот интервал будет округлён до точности системных часов, и задержка ядерного планирования приведёт к тому, что блокирующий интервал может быть меньше). Если значение 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 игнорируется.

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

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

FUTEX_WAIT
    Возвращается 0, если процесс проснулся по сигналу FUTEX_WAKE. Возможные возвращаемые ошибки смотрите в разделе ОШИБКИ.

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

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

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

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

ОШИБКИ

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

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

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

EINTR
    Операция FUTEX_WAIT прервана сигналом (смотрите signal(7)) или побочным пробуждением.

EINVAL
    Неверный аргумент.

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

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

ETIMEDOUT
    Превышено время ожидания в операции FUTEX_WAIT.

EWOULDBLOCK
    Значение op равно FUTEX_WAIT и значение, на которое указывает uaddr, не равно ожидаемому значению val на момент вызова.

ВЕРСИИ

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

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

ЗАМЕЧАНИЯ

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

СМОТРИТЕ ТАКЖЕ
restart_syscall(2), 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.kernel.org/pub/linux/kernel/people/rusty/