.\" -*- coding: UTF-8 -*- .\" Page by b.hubert .\" and Copyright (C) 2015, Thomas Gleixner .\" and Copyright (C) 2015, Michael Kerrisk .\" .\" %%%LICENSE_START(FREELY_REDISTRIBUTABLE) .\" may be freely modified and distributed .\" %%%LICENSE_END .\" .\" Niki A. Rahimi (LTC Security Development, narahimi@us.ibm.com) .\" added ERRORS section. .\" .\" Modified 2004-06-17 mtk .\" Modified 2004-10-07 aeb, added FUTEX_REQUEUE, FUTEX_CMP_REQUEUE .\" .\" FIXME Still to integrate are some points from Torvald Riegel's mail of .\" 2015-01-23: .\" http://thread.gmane.org/gmane.linux.kernel/1703405/focus=7977 .\" .\" FIXME Do we need to add some text regarding Torvald Riegel's 2015-01-24 mail .\" http://thread.gmane.org/gmane.linux.kernel/1703405/focus=1873242 .\" .\"******************************************************************* .\" .\" This file was generated with po4a. Translate the source file. .\" .\"******************************************************************* .TH futex 2 "31 октября 2023 г." "Linux man\-pages 6.06" .SH ИМЯ futex \- быстрая блокировка в пользовательском пространстве .SH LIBRARY Standard C library (\fIlibc\fP, \fI\-lc\fP) .SH СИНТАКСИС .nf .P \fB#include \fP /* определения констант \fBFUTEX_*\fP */ \fB#include \fP /* определения констант \fBSYS_*\fP */ \fB#include \fP .P \fBlong syscall(SYS_futex, uint32_t *\fP\fIuaddr\fP\fB, int \fP\fIfutex_op\fP\fB, uint32_t \fP\fIval\fP\fB,\fP \fB const struct timespec *\fP\fItimeout\fP\fB,\fP\fI \fP /* or: \fBuint32_t \fP\fIval2\fP\fB */\fP \fB uint32_t *\fP\fIuaddr2\fP\fB, uint32_t \fP\fIval3\fP\fB);\fP .fi .P \fINote\fP: glibc provides no wrapper for \fBfutex\fP(), necessitating the use of \fBsyscall\fP(2). .SH ОПИСАНИЕ Системный вызов \fBfutex\fP() предоставляет программам метод для ожидания пока определённое условие не станет истинным. Обычно, этот системный вызов используется блокирующая конструкция в контексте синхронизации общей памяти. При использовании фьютексов основные операции синхронизации выполняются в пространстве пользователя. Программы пользовательского пространства выполняются системный вызов \fBfutex\fP() только когда нужно, чтобы программа вошла в режим ожидания на долгий срок, пока условие не станет истинным. Также \fBfutex\fP() можно использовать для пробуждения процессов или нитей, ожидающих определённого условия. .P A futex is a 32\-bit value\[em]referred to below as a \fIfutex word\fP\[em]whose address is supplied to the \fBfutex\fP() system call. (Futexes are 32 bits in size on all platforms, including 64\-bit systems.) All futex operations are governed by this value. In order to share a futex between processes, the futex is placed in a region of shared memory, created using (for example) \fBmmap\fP(2) or \fBshmat\fP(2). (Thus, the futex word may have different virtual addresses in different processes, but these addresses all refer to the same location in physical memory.) In a multithreaded program, it is sufficient to place the futex word in a global variable shared by all threads. .P .\" Notes from Darren Hart (Dec 2015): .\" Totally ordered with respect futex operations refers to semantics .\" of the ACQUIRE/RELEASE operations and how they impact ordering of .\" memory reads and writes. The kernel futex operations are protected .\" by spinlocks, which ensure that all operations are serialized .\" with respect to one another. .\" .\" This is a lot to attempt to define in this document. Perhaps a .\" reference to linux/Documentation/memory-barriers.txt as a footnote .\" would be sufficient? Or perhaps for this manual, "serialized" would .\" be sufficient, with a footnote regarding "totally ordered" and a .\" pointer to the memory-barrier documentation? .\" FIXME(Torvald Riegel): .\" Eventually we want to have some text in NOTES to satisfy .\" the reference in the following sentence .\" See NOTES for a detailed specification of .\" the synchronization semantics. Когда выполняется операция с фьютексом, запрашивается блокировка нити, которую выполняет ядро только, если слово фьютекса имеет значение, которое передаёт вызывающая нить (в одном из аргументов вызова \fBfutex\fP()) равное ожидаемому значению слова фьютекса. Загрузка значения слова фьютекса, сравнение этого значения с ожидаемым и реальная блокировка выполняется автоматически и будет полностью упорядочена в соответствии с одновременными операциями, выполняемыми другими нитями на тем же словом фьютекса. Таким образом, слово фьютекса используется для обеспечения синхронизации в пользовательском пространстве реализованной через блокировку ядром. По аналогии с атомарной операцией сравнения\-и\-обмена, которая, потенциально, изменяет общую память, блокировка через фьютекс является атомарной операцией сравнения\-и\-блокировки. .P Одним из применений фьютексов является реализация блокировок. Состояние блокировки (т. е., получена или не получена) может быть представлено в виде атомарно доступного флага в общей памяти. При отсутствии конкурентов, нить может получить доступ или изменить состояние блокировки атомарными инструкциями, например атомарно изменяя её значение с не полученной на полученную с помощью атомарной инструкции сравнения\-и\-обмена (эти инструкции целиком выполняются в пользовательском режим, и ядро с состоянием блокировки ничего не делает). С другой стороны, нить может не получить блокировку, так как она уже получена другой нитью. После этого она может передать флаг блокировки в виде слова фьютекса, значением которого будет ожидаемое значение состояния получения в операции ожидания \fBfutex\fP(). Операция \fBfutex\fP() блокируется, только когда блокировка всё ещё имеется (т. е., значение слова фьютекса совпадает с «состояния получения»). При освобождении блокировки нить сначала сбрасывает состояние блокировки в не полученное, а затем вызывает операцию фьютекса, которая пробуждает нить, заблокированную флагом блокировки, используя его как слово фьютекса (в дальнейшем это может быть оптимизировано для устранения ненужных пробуждений). О том, как использовать фьютексы, смотрите \fBfutex\fP(7). .P Кроме основных операций ожидания и пробуждения у фьютексов есть и другие операции, для более сложных случаев применения. .P .\" Заметим, что для использования фьютексов не требуется явных действий по инициализации и удалению; ядро поддерживает фьютексы (т. е., внутренняя часть реализации ядра) только в операции \fBFUTEX_WAIT\fP, описанной далее, обрабатывая определённое слово фьютекса. .SS Аргументы В аргументе \fIuaddr\fP указывает слово фьютекса. На всех платформах фьютексы это целые числа размером в четыре байта, которые должны быть выровнены по четырёх байтовой границе. Операция, выполняемая с фьютексом, задаётся в аргументе \fIfutex_op\fP; какое значение будет задаваться в \fIval\fP, зависит от \fIfutex_op\fP. .P Остальные аргументы (\fItimeout\fP, \fIuaddr2\fP и \fIval3\fP) требуются только для определённых операций с фьютексами и описаны далее. Там, где эти аргументы не нужны, они игнорируются. .P Для некоторых операций блокировки аргументом \fItimeout\fP является указатель на структуру \fItimespec\fP, в которой задаётся время ожидания операции. Однако, несмотря на прототип, показанный выше, для некоторых операций используются только младшие четыре байта этого аргумента вместо целого числа, назначение которого определяется операцией. Для этих операций ядро преобразует значение \fItimeout\fP сначала к \fIunsigned long\fP, затем к \fIuint32_t\fP. Отсюда и до конца страницы этот аргумент будет называться \fIval2\fP, когда он интерпретируется в такой манере. .P Там, где требуется, аргумент \fIuaddr2\fP представляет собой указатель на второе слово фьютекса, которое используется операцией. .P .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Интерпретация последнего целочисленного аргумента, \fIval3\fP, зависит от операции. .SS "Операции с фьютексами" Аргумент \fIfutex_op\fP состоит из двух частей: команды, задающей выполняемую операцию, и объединённые биты нуля или более параметров, которые изменяют поведение операции. Параметры, которые можно включать в \fIfutex_op\fP: .TP \fBFUTEX_PRIVATE_FLAG\fP (начиная с Linux 2.6.22) .\" commit 34f01cc1f512fa783302982776895c73714ebbc2 .\" I.e., It allows the kernel choose the fast path for validating .\" the user-space address and avoids expensive VMA lookups, .\" taking reference counts on file backing store, and so on. Этот параметр может быть использован для всех операций с фьютексами. Он указывает ядру, что фьютекс доступен только для одного процесса и недоступен другим процессам (т. е., используется для синхронизации только между нитями одного процесса). Это позволяет ядру выполнять некоторые дополнительные оптимизации для производительности. .IP .\" except the obsolete FUTEX_FD, for which the "private" flag was .\" meaningless Для удобства, в \fI\fP определён набор констант с суффиксом \fB_PRIVATE\fP, которые эквивалентны всем операциям, перечисленным ниже, но с добавленным константным значением флага \fBFUTEX_PRIVATE_FLAG\fP. То есть, существуют \fBFUTEX_WAIT_PRIVATE\fP, \fBFUTEX_WAKE_PRIVATE\fP и т. д. .TP \fBFUTEX_CLOCK_REALTIME\fP (начиная с Linux 2.6.28) .\" commit 1acdac104668a0834cfa267de9946fac7764d486 .\" commit 337f13046ff03717a9e99675284a817527440a49 .\" commit bf22a6976897977b0a3f1aeba6823c959fc4fdae This option bit can be employed only with the \fBFUTEX_WAIT_BITSET\fP, \fBFUTEX_WAIT_REQUEUE_PI\fP, (since Linux 4.5) \fBFUTEX_WAIT\fP, and (since Linux 5.14) \fBFUTEX_LOCK_PI2\fP operations. .IP Если он указан, то ядро измеряет \fItimeout\fP по часам \fBCLOCK_REALTIME\fP. .IP Если он не указан, то ядро измеряет \fItimeout\fP по часам \fBCLOCK_MONOTONIC\fP. .P .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Операцией в \fIfutex_op\fP может быть одно из: .TP \fBFUTEX_WAIT\fP (начиная с Linux 2.6.0) .\" Strictly speaking, since some time in Linux 2.5.x .\" FIXME: Torvald, I think we may need to add some explanation of .\" "totally ordered" here. Эта операция проверяет, что значение слова фьютекса, на которое указывает адрес \fIuaddr\fP по прежнему содержит ожидаемое значение \fIval\fP и если это так, то засыпает, ожидая операции \fBFUTEX_WAKE\fP для этого слова фьютекса. Загрузка значения слова фьютекса является атомарным доступом к памяти (т. е., используются атомарные машинные инструкции соответствующей архитектуры). Эта загрузка, сравнение с ожидаемым значением и запуск сна выполняются атомарно и целиком упорядочены относительно других фьютекс\-операций с этим словом фьютекса. Если нить начала засыпать, то считается что она — ожидающий этого слова фьютекса. Если значение фьютекса не совпадает с \fIval\fP, то вызов немедленно завершается с ошибкой \fBEAGAIN\fP. .IP Целью сравнения с ожидаемым значением является предотвращение потери пробуждения. Если другая нить изменит значение слова фьютекса после того, как вызывающая нить решила заблокироваться из\-за предыдущего значения и если другая нить выполнила операцию \fBFUTEX_WAKE\fP (или подобное пробуждение) после изменения значения и до этой операции \fBFUTEX_WAIT\fP, то вызывающая нить увидит эту смену значения и не станет впадать в сон. .IP Если значение \fItimeout\fP не равно NULL, то структура, на которую он указывает, определяет время ожидания (этот интервал будет округлён до точности системных часов, и гарантируется, что он не наступит раньше положенного). По умолчанию время ожидания измеряется по часам \fBCLOCK_MONOTONIC\fP, но начиная с Linux 4.5 можно выбрать часы \fBCLOCK_REALTIME\fP, указав \fBFUTEX_CLOCK_REALTIME\fP в \fIfutex_op\fP. Если \fItimeout\fP равно NULL, то вызов блокируется бессрочно. .IP \fIЗамечание\fP: при \fBFUTEX_WAIT\fP значение \fItimeout\fP интерпретируется как \fIотносительное\fP. В этом отличие от других операций над фьютексами, в которых \fItimeout\fP интерпретируется как абсолютное значение. Чтобы получить эквивалент \fBFUTEX_WAIT\fP с абсолютным временем ожидания укажите \fBFUTEX_WAIT_BITSET\fP в \fIval3\fP вместе с \fBFUTEX_BITSET_MATCH_ANY\fP. .IP .\" FIXME . (Torvald) I think we should remove this. Or maybe adapt to a .\" different example. .\" .\" For .\" .BR futex (7), .\" this call is executed if decrementing the count gave a negative value .\" (indicating contention), .\" and will sleep until another process or thread releases .\" the futex and executes the .\" .B FUTEX_WAKE .\" operation. .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Аргументы \fIuaddr2\fP и \fIval3\fP игнорируются. .TP \fBFUTEX_WAKE\fP (начиная с Linux 2.6.0) .\" Strictly speaking, since Linux 2.5.x Эта операция пробуждает не больше \fIval\fP процессов, ожидающих (например, внутри \fBFUTEX_WAIT\fP) слово фьютекса по адресу \fIuaddr\fP. Чаще всего, \fIval\fP присваивают или 1 (пробудить одного ожидающего), или \fBINT_MAX\fP (пробудить всех ожидающих). Не гарантируется, что разбудят каких\-то определённых ожидающих (например, что ожидающий с большим приоритетом планировщика будет разбужен раньше ожидающего, имеющего меньший приоритет). .IP .\" FIXME . (Torvald) I think we should remove this. Or maybe adapt to .\" a different example. .\" .\" For .\" .BR futex (7), .\" this is executed if incrementing the count showed that .\" there were waiters, .\" once the futex value has been set to 1 .\" (indicating that it is available). .\" .\" How does "incrementing the count show that there were waiters"? .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Аргументы \fItimeout\fP, \fIuaddr2\fP и \fIval3\fP игнорируются. .TP \fBFUTEX_FD\fP (начиная с Linux 2.6.0 и по Linux 2.6.25 включительно) .\" Strictly speaking, from Linux 2.5.x to Linux 2.6.25 Эта операция создаёт файловый дескриптор, который связан с фьютексом по адресу \fIuaddr\fP. Вызывающий должен закрыть возвращённый файловый дескриптор после использования. Если другой процесс или нить выполняет операцию \fBFUTEX_WAKE\fP со словом фьютекса, то файловый дескриптор будет отмечен как доступный для чтения в \fBselect\fP(2), \fBpoll\fP(2) и \fBepoll\fP(7). .IP Файловый дескриптор можно использовать для получения асинхронных уведомлений: если \fIval\fP не равно нулю, то когда другой процесс или нить выполняют \fBFUTEX_WAKE\fP, то вызывающий примет сигнал с номером, который был указан в \fIval\fP. .IP Аргументы \fItimeout\fP, \fIuaddr2\fP и \fIval3\fP игнорируются. .IP .\" commit 82af7aca56c67061420d618cc5a30f0fd4106b80 .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Так как по своей природе операция \fBFUTEX_FD\fP приводит к состязательности, она была удалена из Linux, начиная с версии 2.6.26. .TP \fBFUTEX_REQUEUE\fP (начиная с Linux 2.6.0) .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Эта операция выполняет ту же задачу, что и \fBFUTEX_CMP_REQUEUE\fP (смотрите далее), за исключением того, что она не проверяет используемое значение в \fIval3\fP (аргумент \fIval3\fP игнорируется). .TP \fBFUTEX_CMP_REQUEUE\fP (начиная с Linux 2.6.7) Сначала эта операция проверяет, что по адресу \fIuaddr\fP по прежнему содержится значение \fIval3\fP. Если нет, то операция завершается с ошибкой \fBEAGAIN\fP. В противном случае, операция пробуждает не более \fIval\fP ожидающих, которые ждут фьютекс по адресу \fIuaddr\fP. Если существует более \fIval\fP ожидающих, то оставшиеся ожидающие удаляются из очереди ожидания фьютекса\-источника по адресу \fIuaddr\fP и добавляются в очередь ожидания фьютекса\-назначения по адресу \fIuaddr2\fP. В аргументе \fIval2\fP задаётся верхний предел количества ожидающих, которые перемещаются в очередь фьютекса по адресу \fIuaddr2\fP. .IP .\" FIXME(Torvald) Is the following correct? Or is just the decision .\" which threads to wake or requeue part of the atomic operation? .\" Notes from a f2f conversation with Thomas Gleixner (Aug 2015): ### .\" The operation is serialized with respect to operations on both .\" source and target futex. No other waiter can enqueue itself .\" for waiting and no other waiter can dequeue itself because of .\" a timeout or signal. Загрузка из \fIuaddr\fP является атомарным доступом к памяти (т. е., используются атомарные машинные инструкции соответствующей архитектуры). Эта загрузка, сравнение с \fIval3\fP и перестановка в очередь ожидающих выполняются атомарно и целиком упорядочены относительно других фьютекс\-операций с этим словом фьютекса. .IP Типичными значениями \fIval\fP являются 0 или 1 (указание \fBINT_MAX\fP бесполезно, так как это сделало бы операцию \fBFUTEX_CMP_REQUEUE\fP эквивалентной \fBFUTEX_WAKE\fP). Значение ограничения, указанное в \fIval2\fP, обычно, или 1 или \fBINT_MAX\fP (указание 0 бесполезно, так как это сделало бы операцию \fBFUTEX_CMP_REQUEUE\fP эквивалентной \fBFUTEX_WAIT\fP). .IP .\" But, as Rich Felker points out, there remain valid use cases for .\" FUTEX_REQUEUE, for example, when the calling thread is requeuing .\" the target(s) to a lock that the calling thread owns .\" From: Rich Felker .\" Date: Wed, 29 Oct 2014 22:43:17 -0400 .\" To: Darren Hart .\" CC: libc-alpha@sourceware.org, ... .\" Subject: Re: Add futex wrapper to glibc? Операция \fBFUTEX_CMP_REQUEUE\fP была добавлена в качестве замены имевшейся \fBFUTEX_REQUEUE\fP. Различие в том, что проверку значения по адресу \fIuaddr\fP можно использовать для гарантии того, что перестановка в очередь произойдёт только при определённых условиях, что в определённых случаях позволит избежать состязательности. .IP И \fBFUTEX_REQUEUE\fP и \fBFUTEX_CMP_REQUEUE\fP можно использовать для недопущения «нашествия орды» из пробудившихся, которое может произойти при использовании \fBFUTEX_WAKE\fP в случаях, когда всем разбуженным ожидающим требуется заблокировать другой фьютекс. Рассмотрим следующий сценарий, где несколько ожидающих нитей ждут B, очередь ожидания реализована с помощью фьютекса: .IP .in +4n .EX lock(A) while (!check_value(V)) { unlock(A); block_on(B); lock(A); }; unlock(A); .EE .in .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Если пробуждающая нить использует \fBFUTEX_WAKE\fP, то все ожидающие,ждущие B, проснулись бы, и попытались получить блокировку A. Однако пробуждение всех нитей таким образом было бы нецелесообразно, так как все кроме одной нити снова немедленно бы заблокировались в ожидании A. В отличие от этого, операция перестановки в очередь разбудит только одного ожидающего и переместит остальных ожидающих в ожидание блокировки A, и когда разбуженный ожидающий разблокирует A, то следующий ожидающий сможет продолжить работу. .TP \fBFUTEX_WAKE_OP\fP (начиная с Linux 2.6.14) .\" commit 4732efbeb997189d9f9b04708dc26bf8613ed721 .\" Author: Jakub Jelinek .\" Date: Tue Sep 6 15:16:25 2005 -0700 .\" FIXME. (Torvald) The glibc condvar implementation is currently being .\" revised (e.g., to not use an internal lock anymore). .\" It is probably more future-proof to remove this paragraph. .\" [Torvald, do you have an update here?] Эта операция была добавлена для работы в некоторых случаях из пользовательского пространства, в которых нужно одновременно учитывать несколько фьютексов. Самый известный пример — реализация \fBpthread_cond_signal\fP(3), которая требует операций для работы с двумя фьютексами: один для реализации мьютекса, а другой для реализации очереди ожидания, связанной с переменной условия. Операция \fBFUTEX_WAKE_OP\fP позволяет это реализовать без увеличения состязательности и контекстного переключения. .IP Операция \fBFUTEX_WAKE_OP\fP эквивалентна выполнению следующего кода, при чём, атомарно и полностью упорядочено в соответствии с другими фьютекс\-операциями, выполняемыми над двумя указанными словами фьютекса: .IP .in +4n .EX uint32_t oldval = *(uint32_t *) uaddr2; *(uint32_t *) uaddr2 = oldval \fIop\fP \fIoparg\fP; futex(uaddr, FUTEX_WAKE, val, 0, 0, 0); if (oldval \fIcmp\fP \fIcmparg\fP) futex(uaddr2, FUTEX_WAKE, val2, 0, 0, 0); .EE .in .IP Иначе говоря, \fBFUTEX_WAKE_OP\fP делает следующее: .RS .IP \[bu] 3 сохраняет первоначальное значение слова фьютекса по адресу \fIuaddr2\fP и выполняет операцию изменения значения фьютекса по адресу \fIuaddr2\fP; это атомарная операция с памятью по чтению\-изменению\-записи (т. е., используются атомарные машинные инструкции на соответствующей архитектуре); .IP \[bu] пробуждает не более \fIval\fP ожидающих у фьютекса слова фьютекса по адресу \fIuaddr\fP; и .IP \[bu] в зависимости от результата проверки первоначального значения слова фьютекса по адресу \fIuaddr2\fP, пробуждает не более \fIval2\fP ожидающих у фьютекса слова фьютекса по адресу \fIuaddr2\fP. .RE .IP Операция и сравнение, которое будет выполнено, кодируется в битах аргумента \fIval3\fP. Графически, кодирование выглядит так: .IP .in +4n .EX +\-\-\-+\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+ |оп |сра| аргоп | аргсра | +\-\-\-+\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+ 4 4 12 12 <== кол\-во бит .EE .in .IP В виде кода это выглядит так: .IP .in +4n .EX #define FUTEX_OP(op, oparg, cmp, cmparg) \e (((op & 0xf) << 28) | \e ((cmp & 0xf) << 24) | \e ((oparg & 0xfff) << 12) | \e (cmparg & 0xfff)) .EE .in .IP Указанные выше \fIop\fP и \fIcmp\fP могут содержат один из кодов, перечисленных далее. Компоненты \fIoparg\fP и \fIcmparg\fP являются числовыми литералами, с учётом замечаний далее. .IP Компонент \fIop\fP может иметь одно из следующих значений: .IP .in +4n .EX FUTEX_OP_SET 0 /* uaddr2 = oparg; */ FUTEX_OP_ADD 1 /* uaddr2 += oparg; */ FUTEX_OP_OR 2 /* uaddr2 |= oparg; */ FUTEX_OP_ANDN 3 /* uaddr2 &= \[ti]oparg; */ FUTEX_OP_XOR 4 /* uaddr2 \[ha]= oparg; */ .EE .in .IP Также, битовое сложение следующего значения с \fIop\fP приводит к использованию \fI(1\ <<\~oparg)\fP в качестве операнда: .IP .in +4n .EX FUTEX_OP_ARG_SHIFT 8 /* исп. (1 << oparg) как операнд */ .EE .in .IP В поле \fIcmp\fP может быть одно из: .IP .in +4n .EX FUTEX_OP_CMP_EQ 0 /* если (oldval == cmparg) — пробудить */ FUTEX_OP_CMP_NE 1 /* если (oldval != cmparg) — пробудить */ FUTEX_OP_CMP_LT 2 /* если (oldval < cmparg) — пробудить */ FUTEX_OP_CMP_LE 3 /* если (oldval <= cmparg) — пробудить */ FUTEX_OP_CMP_GT 4 /* если (oldval > cmparg) — пробудить */ FUTEX_OP_CMP_GE 5 /* если (oldval >= cmparg) — пробудить */ .EE .in .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Возвращаемое значение операции \fBFUTEX_WAKE_OP\fP — сумма количества разбуженных ожидающих фьютекса \fIuaddr\fP и количества разбуженных ожидающих фьютекса \fIuaddr2\fP. .TP \fBFUTEX_WAIT_BITSET\fP (начиная с Linux 2.6.25) .\" commit cd689985cf49f6ff5c8eddc48d98b9d581d9475d Эта операция подобна \fBFUTEX_WAIT\fP, за исключением того, что \fIval3\fP используется для передачи 32\-битной маски в ядро. Данная битовая маска, в которой должен быть установлен хотя бы один бит, хранится в ядре во внутреннем состоянии ожидающего. Подробности смотрите в описании \fBFUTEX_WAKE_BITSET\fP. .IP Если значение \fItimeout\fP не равно NULL, то структура, на которую он указывает, определяет абсолютное время ожидания. Если \fItimeout\fP равно NULL, то операция блокируется бессрочно. .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Аргумент \fIuaddr2\fP игнорируется. .TP \fBFUTEX_WAKE_BITSET\fP (начиная с Linux 2.6.25) .\" commit cd689985cf49f6ff5c8eddc48d98b9d581d9475d Данная операция подобна \fBFUTEX_WAKE\fP, за исключением того, что \fIval3\fP используется для передачи 32\-битной маски в ядро. Данная битовая маска, в которой должен быть установлен хотя бы один бит, используется для выбора ожидающих, которые должны быть разбужены. Выбор выполняется путём побитового И битовой маски «wake» (т. е., значения \fIval3\fP) и битовой маски, которая хранится в ядре во внутреннем состоянии ожидающего (битовая маска «wait», устанавливающаяся с помощью \fBFUTEX_WAIT_BITSET\fP). Все ожидающие, для которых результат побитового И не равен нулю, пробуждаются; оставшиеся ожидающие продолжают спать. .IP .\" According to http://locklessinc.com/articles/futex_cheat_sheet/: .\" .\" "The original reason for the addition of these extensions .\" was to improve the performance of pthread read-write locks .\" in glibc. However, the pthreads library no longer uses the .\" same locking algorithm, and these extensions are not used .\" without the bitset parameter being all ones. .\" .\" The page goes on to note that the FUTEX_WAIT_BITSET operation .\" is nevertheless used (with a bit mask of all ones) in order to .\" obtain the absolute timeout functionality that is useful .\" for efficiently implementing Pthreads APIs (which use absolute .\" timeouts); FUTEX_WAIT provides only relative timeouts. Влияние \fBFUTEX_WAIT_BITSET\fP и \fBFUTEX_WAKE_BITSET\fP позволяет выбирать пробуждающихся из многих ожидающих, которые заблокированы на один фьютекс. Однако заметим, что в зависимости от варианта применения, использование данного свойства комбинирования битовой маски с фьютексом может быть менее эффективно, чем простое использование нескольких фьютексов, так как использование комбинирования битовой маски требует от ядра проверки всех ожидающих фьютекса, включая тех, которые и не нужно было бы будить (т. е., у них неподходящий набор бит в их битовой маске «wait»). .IP Константу \fBFUTEX_BITSET_MATCH_ANY\fP, которая соответствует всем установленным битам в 32\-битной маске, можно использовать в аргументе \fIval3\fP для \fBFUTEX_WAIT_BITSET\fP и \fBFUTEX_WAKE_BITSET\fP. Кроме различий в обработке аргумента \fItimeout\fP, операция \fBFUTEX_WAIT\fP эквивалентна \fBFUTEX_WAIT_BITSET\fP с \fIval3\fP, равным \fBFUTEX_BITSET_MATCH_ANY\fP, то есть разрешается будить любого пробуждающего. Операция \fBFUTEX_WAKE\fP эквивалентна \fBFUTEX_WAKE_BITSET\fP с \fIval3\fP, равным \fBFUTEX_BITSET_MATCH_ANY\fP, то есть пробуждается любой(ые) пробуждающий. .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Аргументы \fIuaddr2\fP и \fItimeout\fP игнорируются. .SS "Наследование приоритета из\-за фьютексов" В Linux поддерживается наследование приоритета из\-за фьютексов priority\-inheritance (PI), так как требуется решать проблему обратного приоритета, которая встречается у обычных блокировок фьютексов. Проблема обратного приоритета возникает, когда задача с высоким приоритетом блокируется в ожидании получения блокировки, которую удерживает задача с низким приоритетом, в то время как задачи со средним приоритетом постоянно вытесняют задачу с низким приоритетом с ЦП. В следствии этого, выполнение задачи с низким приоритетом никак не продвигается к освобождению блокировки, и задача с высоким приоритетом остаётся заблокированной. .P Наследование приоритета — это механизм, который решает проблему обратного приоритета. С его помощью, когда задача с высоким приоритетом блокируется из\-за удержания блокировки задачей с низким приоритетом, приоритет задачи с низким приоритетом временно повышается до приоритета, имеющегося у заблокированной задачи, и поэтому не происходит вытеснения задачами с средним приоритетом, что способствует ускорению освобождения блокировки. Чтобы это работало, наследование приоритета должно быть транзитивным, то есть если задача с высоким приоритетом заблокирована, из\-за удержания блокировки задачей с низким приоритетом, которая сама заблокирована из\-за удержания блокировки другой задачей со средним приоритетом (и так далее, по цепочке произвольной длины), то для обеих этих задач (или, шире, всех задач в заблокированной цепочке) повышается приоритет, который равен приоритету задачи с высоким приоритетом. .P .\" .\" Quoting Darren Hart: .\" These opcodes paired with the PI futex value policy (described below) .\" defines a "futex" as PI aware. These were created very specifically .\" in support of PI pthread_mutexes, so it makes a lot more sense to .\" talk about a PI aware pthread_mutex, than a PI aware futex, since .\" there is a lot of policy and scaffolding that has to be built up .\" around it to use it properly (this is what a PI pthread_mutex is). Со стороны пользовательского пространства фьютекс является PI из\-за следования соглашениям (описанных далее) между пользовательским пространством и ядром о значении слова фьютекса и применяемым операциям над PI\-фьютексами, описанным далее (в отличие от других операций с фьютексами, описанных выше, операции с PI\-фьютексами разработаны для реализации очень специфичных механизмов IPC). .P .\" mtk: The following text is drawn from the Hart/Guniguntala paper .\" (listed in SEE ALSO), but I have reworded some pieces .\" significantly. .\" Операции с PI\-фьютексами, описанные далее, отличаются от других операций с фьютексами в том, что они следуют политике использования значения слова фьютекса: .IP \[bu] 3 Если блокировка не получена, то значение слова фьютекса должно быть равно 0. .IP \[bu] Если блокировка получена, то значение слова фьютекса должно быть равно ID нити (TID; смотрите \fBgettid\fP(2)), которой оно принадлежит. .IP \[bu] Если блокировка получена и есть претендующие на неё нити, то в значении слова фьютекса должен быть установлен бит \fBFUTEX_WAITERS\fP; иначе говоря, это значение равно: .IP .in +4n .EX FUTEX_WAITERS | TID .EE .in .IP (заметим, что некорректное слово PI\-фьютекса не имеет владельца и \fBFUTEX_WAITERS\fP) .P С этой действующей политикой приложение пространства пользователя может получить свободную блокировку или освободить блокировку с помощью атомарных инструкций, выполняемых в пользовательском режиме (например, операцией сравнение\-и\-обмен \fIcmpxchg\fP на архитектуре x86). Получение блокировки состоит просто из использования сравнения\-и\-обмена для атомарного изменения значения слова фьютекса на TID вызывающего, если предыдущее значение было равно 0. Для освобождения блокировки требуется использовать сравнение\-и\-обмен для изменения значения слова фьютекса на 0, если предыдущее значение равно ожидаемому TID. .P If a futex is already acquired (i.e., has a nonzero value), waiters must employ the \fBFUTEX_LOCK_PI\fP or \fBFUTEX_LOCK_PI2\fP operations to acquire the lock. If other threads are waiting for the lock, then the \fBFUTEX_WAITERS\fP bit is set in the futex value; in this case, the lock owner must employ the \fBFUTEX_UNLOCK_PI\fP operation to release the lock. .P В случаях, когда вызывающие переходят в ядро (т. е., требуется выполнение вызова \fBfutex\fP()), после этого они напрямую работают с так называемым RT\-мьютексом, механизмом блокировок ядра, которым реализована требуемая семантика наследования приоритета. После получения RT\-мьютекса, значение фьютекса обновляется соответствующим образом, перед возврата вызывающей нити в пространство пользователя. .P .\" tglx (July 2015): .\" If there are multiple waiters on a pi futex then a wake pi operation .\" will wake the first waiter and hand over the lock to this waiter. This .\" includes handing over the rtmutex which represents the futex in the .\" kernel. The strict requirement is that the futex owner and the rtmutex .\" owner must be the same, except for the update period which is .\" serialized by the futex internal locking. That means the kernel must .\" update the user-space value prior to returning to user space Важно упомянуть, что ядро обновит значение слова фьютекса до возврата в пространство пользователя (это предотвращает возможность попадания значения слова фьютекса в некорректное состояние, такое, что имея владельца, значение равно 0, или имея ожидающих, не установлен бит \fBFUTEX_WAITERS\fP). .P .\" tglx (July 2015): .\" The FUTEX_OWNER_DIED bit can also be set on uncontended futexes, where .\" the kernel has no state associated. This happens via the robust futex .\" mechanism. In that case the futex value will be set to .\" FUTEX_OWNER_DIED. The robust futex mechanism is also available for non .\" PI futexes. Если фьютекс связан с RT\-мьютексом в ядре (т. е., есть заблокированные ожидающие) и владелец фьютекса/RT\-мьютекса неожиданно завершился, то ядро очищает RT\-мьютекс и передаёт его следующему ожидающему. Это, в свою очередь, требует, чтобы значение в пользовательском пространстве было изменено соответствующим образом. Для сообщения о необходимости этого ядро изменяет бит \fBFUTEX_OWNER_DIED\fP в слове фьютекса вместе со сменой ID нити нового владельца. Пользовательское пространство может определить такую ситуацию по установленному биту \fBFUTEX_OWNER_DIED\fP и затем, соответствующим образом, очистить устаревшее состояние, возникшее из\-за закончившего работу владельца. .P PI\-фьютексы обрабатываются при указании в \fIfutex_op\fP одного из значений, перечисленных далее. Заметим, что операции с PI\-фьютексами должны использовать попарно и учитывать некоторые дополнительные требования: .IP \[bu] 3 Парой к операциям \fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP и \fBFUTEX_TRYLOCK_PI\fP является \fBFUTEX_UNLOCK_PI\fP. Операция \fBFUTEX_UNLOCK_PI\fP должна применяться только к фьютексам, принадлежащим вызывающей нити, определённой значением политики, или же возникнет ошибка \fBEPERM\fP. .IP \[bu] Парой к операции \fBFUTEX_WAIT_REQUEUE_PI\fP является \fBFUTEX_CMP_REQUEUE_PI\fP. Она должна применяться для перехода с не PI\-фьютекса к PI\-фьютексу (или возникает ошибка \fBEINVAL\fP). Также, \fIval\fP (количество разбуживаемых ожидающих) должно равняться 1 (или возникает ошибка \fBEINVAL\fP). .P .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Операции с PI\-фьютексами: .TP \fBFUTEX_LOCK_PI\fP (начиная с Linux 2.6.18) .\" commit c87e2837be82df479a6bae9f155c43516d2feebc This operation is used after an attempt to acquire the lock via an atomic user\-mode instruction failed because the futex word has a nonzero value\[em]specifically, because it contained the (PID\-namespace\-specific) TID of the lock owner. .IP .\" tglx (July 2015): .\" The operation here is similar to the FUTEX_WAIT logic. When the user .\" space atomic acquire does not succeed because the futex value was non .\" zero, then the waiter goes into the kernel, takes the kernel internal .\" lock and retries the acquisition under the lock. If the acquisition .\" does not succeed either, then it sets the FUTEX_WAITERS bit, to signal .\" the lock owner that it needs to go into the kernel. Here is the pseudo .\" code: .\" .\" lock(kernel_lock); .\" retry: .\" .\" /* .\" * Owner might have unlocked in user space before we .\" * were able to set the waiter bit. .\" */ .\" if (atomic_acquire(futex) == SUCCESS) { .\" unlock(kernel_lock()); .\" return 0; .\" } .\" .\" /* .\" * Owner might have unlocked after the above atomic_acquire() .\" * attempt. .\" */ .\" if (atomic_set_waiters_bit(futex) != SUCCESS) .\" goto retry; .\" .\" queue_waiter(); .\" unlock(kernel_lock); .\" block(); .\" Операция проверяет значение слова фьютекса по адресу \fIuaddr\fP. Если значение равно 0, то ядро пытается атомарно изменить слово фьютекса на TID вызывающего. Если слово фьютекса не равно нулю, то ядро атомарно устанавливает бит \fBFUTEX_WAITERS\fP, который указывает владельцу фьютекса, что он не может разблокировать фьютекс в пространстве пользователя атомарным способом посредством установки значения фьюетекса в 0. После этого ядро: .RS .IP (1) 5 Пытается найти нить\-владельца по TID. .IP (2) Создаёт или повторно использует состояние ядра от имени владельца (если это первый ожидающий, то для этого фьютекса не состояния ядра, поэтому состояние ядра создаётся блокировкой RT\-мьютекса и владелец фьютекса становится владельцем RT\-мьютекса. Если ожидающие уже есть, то используется имеющееся состояние). .IP (3) Присоединяет ожидающего к фьютексу (т. е., ожидающий ставится в очередь списка ожидающих на основе RT\-мьютекса). .RE .IP .\" August 2015: .\" mtk: If the realm is restricted purely to SCHED_OTHER (SCHED_NORMAL) .\" processes, does the nice value come into play also? .\" .\" tglx: No. SCHED_OTHER/NORMAL tasks are handled in FIFO order .\" (i.e., task 1 blocks on lock A, held by task 2, .\" while task 2 blocks on lock B, held by task 3) Если есть более одного ожидающего, то перестановка ожидающего в очередь выполняется в порядке убывания приоритета (упорядочивание по приоритету описано в разделе об алгоритмах планирования \fBSCHED_DEADLINE\fP, \fBSCHED_FIFO\fP и \fBSCHED_RR\fP в \fBsched\fP(7)). Владелец наследует от ожидающего пропускную способность ЦП (если ожидающий работает по алгоритму планирования \fBSCHED_DEADLINE\fP) или приоритет (если ожидающий работает по алгоритму планирования \fBSCHED_RR\fP или \fBSCHED_FIFO\fP). При обнаружении вложенности блокировки и клинча такое наследование распространяется по всей цепочке блокировки. .IP .\" 2016-07-07 response from Thomas Gleixner on LKML: .\" From: Thomas Gleixner .\" Date: 6 July 2016 at 20:57 .\" Subject: Re: futex: Allow FUTEX_CLOCK_REALTIME with FUTEX_WAIT op .\" .\" On Thu, 23 Jun 2016, Michael Kerrisk (man-pages) wrote: .\" > On 06/23/2016 08:28 PM, Darren Hart wrote: .\" > > And as a follow-on, what is the reason for FUTEX_LOCK_PI only using .\" > > CLOCK_REALTIME? It seems reasonable to me that a user may want to wait a .\" > > specific amount of time, regardless of wall time. .\" > .\" > Yes, that's another weird inconsistency. .\" .\" The reason is that phtread_mutex_timedlock() uses absolute timeouts based on .\" CLOCK_REALTIME. glibc folks asked to make that the default behaviour back .\" then when we added LOCK_PI. В аргументе \fItimeout\fP задаётся время ожидания захвата блокировки. Если \fItimeout\fP равно NULL, то структура, на которую он указывает, определяет абсолютное время ожидания, отсчитываемое по часам \fBCLOCK_REALTIME\fP. Если \fItimeout\fP равно NULL, то операция может быть в блокировке неопределённо долго. .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Аргументы \fIuaddr2\fP, \fIval\fP и \fIval3\fP игнорируются. .TP \fBFUTEX_LOCK_PI2\fP (начиная с Linux 5.14) .\" commit bf22a6976897977b0a3f1aeba6823c959fc4fdae .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" This operation is the same as \fBFUTEX_LOCK_PI\fP, except that the clock against which \fItimeout\fP is measured is selectable. By default, the (absolute) timeout specified in \fItimeout\fP is measured against the \fBCLOCK_MONOTONIC\fP clock, but if the \fBFUTEX_CLOCK_REALTIME\fP flag is specified in \fIfutex_op\fP, then the timeout is measured against the \fBCLOCK_REALTIME\fP clock. .TP \fBFUTEX_TRYLOCK_PI\fP (начиная с Linux 2.6.18) .\" commit c87e2837be82df479a6bae9f155c43516d2feebc Эта операция пытается получить блокировку по адресу \fIuaddr\fP. Она вызывается, когда не удалось выполнить атомарное получение из пользовательского пространства, так как слово фьютекса не равно 0. .IP .\" Paraphrasing a f2f conversation with Thomas Gleixner about the .\" above point (Aug 2015): ### .\" There is a rare possibility of a race condition involving an .\" uncontended futex with no owner, but with waiters. The .\" kernel-user-space contract is that if a futex is nonzero, you must .\" go into kernel. The futex was owned by a task, and that task dies .\" but there are no waiters, so the futex value is non zero. .\" Therefore, the next locker has to go into the kernel, .\" so that the kernel has a chance to clean up. (CMXCH on zero .\" in user space would fail, so kernel has to clean up.) .\" Darren Hart (Oct 2015): .\" The trylock in the kernel has more state, so it can independently .\" verify the flags that user space must trust implicitly. Так как ядро имеет больший доступ к информации о состоянии, чем пользовательское пространство, получение блокировки из ядра может осуществиться, в случаях когда слово фьютекса (т. е., информация о состоянии доступна из пользовательского пространства) устарело (\fBFUTEX_WAITERS\fP и/или \fBFUTEX_OWNER_DIED\fP). Это может случиться, если владелец фьютекса неожиданно завершился. Пользовательское пространство не может учесть это событие не получив состязательности, но ядро может решить данную проблему и получить блокировку. .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Аргументы \fIuaddr2\fP, \fIval\fP, \fItimeout\fP и \fIval3\fP игнорируются. .TP \fBFUTEX_UNLOCK_PI\fP (начиная с Linux 2.6.18) .\" commit c87e2837be82df479a6bae9f155c43516d2feebc This operation wakes the top priority waiter that is waiting in \fBFUTEX_LOCK_PI\fP or \fBFUTEX_LOCK_PI2\fP on the futex address provided by the \fIuaddr\fP argument. .IP Операция вызывается, когда значение по адресу \fIuaddr\fP пользовательского пространства невозможно изменить атомарно с TID (владельца) на 0. .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Аргументы \fIuaddr2\fP, \fIval\fP, \fItimeout\fP и \fIval3\fP игнорируются. .TP \fBFUTEX_CMP_REQUEUE_PI\fP (начиная с Linux 2.6.31) .\" commit 52400ba946759af28442dee6265c5c0180ac7122 Данная операция является PI\-аналогом операции \fBFUTEX_CMP_REQUEUE\fP. Она перестанавливает ожидающих, заблокированных с помощью \fBFUTEX_WAIT_REQUEUE_PI\fP для \fIuaddr\fP, из очереди не PI\-фьютекса источника (\fIuaddr\fP) в очередь PI\-фьютекса назначения (\fIuaddr2\fP). .IP Как и \fBFUTEX_CMP_REQUEUE\fP, эта операция пробуждает не более \fIval\fP ожидающих, которые ждут фьютекса по адресу \fIuaddr\fP. Однако у \fBFUTEX_CMP_REQUEUE_PI\fP значение \fIval\fP должно быть равно 1 (чтобы избежать «нашествия орды»). Оставшиеся ожидающие удаляются из очереди ожидания фьютекса\-источника по адресу \fIuaddr\fP и добавляются в очередь ожидания фьютекса\-назначения по адресу \fIuaddr2\fP. .IP .\" val2 is the cap on the number of requeued waiters. .\" In the glibc pthread_cond_broadcast() implementation, this argument .\" is specified as INT_MAX, and for pthread_cond_signal() it is 0. .\" .\" The page at http://locklessinc.com/articles/futex_cheat_sheet/ .\" notes that "priority-inheritance Futex to priority-inheritance .\" Futex requeues are currently unsupported". However, probably .\" the page does not need to say nothing about this, since .\" Thomas Gleixner commented (July 2015): "they never will be .\" supported because they make no sense at all" .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Аргументы \fIval2\fP и \fIval3\fP служат тем же целям, что и в \fBFUTEX_CMP_REQUEUE\fP. .TP \fBFUTEX_WAIT_REQUEUE_PI\fP (начиная с Linux 2.6.31) .\" commit 52400ba946759af28442dee6265c5c0180ac7122 .\" Ждать не PI\-фьютекса по адресу \fIuaddr\fP и, потенциально быть перемещённым в очередь (при операции \fBFUTEX_CMP_REQUEUE_PI\fP из другой задачи) PI\-фьютекса по адресу \fIuaddr2\fP. Операция ожидания на адресе \fIuaddr\fP такая же как для \fBFUTEX_WAIT\fP. .IP Ожидающий может быть удалён из ожидающих на адресе \fIuaddr\fP перемещения в очередь у \fIuaddr2\fP при операции \fBFUTEX_WAKE\fP из другой задачи. В этом случае операция \fBFUTEX_WAIT_REQUEUE_PI\fP завершается с ошибкой \fBEAGAIN\fP. .IP Если значение \fItimeout\fP не равно NULL, то структура, на которую он указывает, определяет абсолютное время ожидания. Если \fItimeout\fP равно NULL, то операция блокируется бессрочно. .IP Аргумент \fIval3\fP игнорируется. .IP .\" .\" Darren Hart notes that a patch to allow glibc to fully support .\" PI-aware pthreads condition variables has not yet been accepted into .\" glibc. The story is complex, and can be found at .\" https://sourceware.org/bugzilla/show_bug.cgi?id=11588 .\" Darren notes that in the meantime, the patch is shipped with various .\" PREEMPT_RT-enabled Linux systems. .\" .\" Related to the preceding, Darren proposed that somewhere, man-pages .\" should document the following point: .\" .\" While the Linux kernel, since Linux 2.6.31, supports requeueing of .\" priority-inheritance (PI) aware mutexes via the .\" FUTEX_WAIT_REQUEUE_PI and FUTEX_CMP_REQUEUE_PI futex operations, .\" the glibc implementation does not yet take full advantage of this. .\" Specifically, the condvar internal data lock remains a non-PI aware .\" mutex, regardless of the type of the pthread_mutex associated with .\" the condvar. This can lead to an unbounded priority inversion on .\" the internal data lock even when associating a PI aware .\" pthread_mutex with a condvar during a pthread_cond*_wait .\" operation. For this reason, it is not recommended to rely on .\" priority inheritance when using pthread condition variables. .\" .\" The problem is that the obvious location for this text is .\" the pthread_cond*wait(3) man page. However, such a man page .\" does not currently exist. .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Операции \fBFUTEX_WAIT_REQUEUE_PI\fP и \fBFUTEX_CMP_REQUEUE_PI\fP добавлены для довольно узкого варианта применения — поддержки переменных условия нитей POSIX с наследованием приоритета. Идея в том, что эти операции всегда должны использоваться попарно, для поддержания синхронизации между пользовательским пространством и ядром. То есть в операции \fBFUTEX_WAIT_REQUEUE_PI\fP приложение пользовательского пространства заранее задаёт назначение для перемещения в очередь, которое проводится операцией \fBFUTEX_CMP_REQUEUE_PI\fP. .SH "ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ" In the event of an error (and assuming that \fBfutex\fP() was invoked via \fBsyscall\fP(2)), all operations return \-1 and set \fIerrno\fP to indicate the error. .P При успешном выполнении возвращаемое значение зависит от операции: .TP \fBFUTEX_WAIT\fP Значение 0 возвращается, если вызывающий был разбужен. Заметим, что пробуждение также может быть вызвано часто используемыми вариантами использования фьютексов в не связанном коде, которое случается, если память под слово фьютекса использовалась ранее (например, при типичной реализации фьютексов на основе мьютексов Pthreads это может возникать при определённых условиях). Поэтому вызывающий всегда должен консервативно предполагать, что возвращаемое значение 0 может означать побочное пробуждение (spurious wake\-up), и учитывать значение слово фьютекса (т. е. схема с синхронизацией пользовательского пространства) при принятии решения нужна дальнейшее ожидание или нет. .TP \fBFUTEX_WAKE\fP Возвращается количество разбуженных ожидающих. .TP \fBFUTEX_FD\fP Возвращается новый файловый дескриптор, связанный с фьютексом. .TP \fBFUTEX_REQUEUE\fP Возвращается количество разбуженных ожидающих. .TP \fBFUTEX_CMP_REQUEUE\fP Возвращается общее количество ожидающих, которые будятся или перемещаются в очередь фьютекса, у которого слово фьютекса задано по адресу \fIuaddr2\fP. Если это значение больше чем \fIval\fP, то разница это количество ожидающих, перемещённых в очередь фьютекса, у которого слово фьютекса задано по адресу \fIuaddr2\fP. .TP \fBFUTEX_WAKE_OP\fP Возвращается общее количество разбуженных ожидающих. Это сумма разбуженных ожидающих у двух фьютексов для слов фьютексов по адресам \fIuaddr\fP и \fIuaddr2\fP. .TP \fBFUTEX_WAIT_BITSET\fP Возвращается 0, если вызывающий был разбужен. Смотрите в описании \fBFUTEX_WAIT\fP, как это правильно учитывать на практике. .TP \fBFUTEX_WAKE_BITSET\fP Возвращается количество разбуженных ожидающих. .TP \fBFUTEX_LOCK_PI\fP Возвращается 0, если фьютекс был успешно заблокирован. .TP \fBFUTEX_LOCK_PI2\fP Возвращается 0, если фьютекс был успешно заблокирован. .TP \fBFUTEX_TRYLOCK_PI\fP Возвращается 0, если фьютекс был успешно заблокирован. .TP \fBFUTEX_UNLOCK_PI\fP Возвращается 0, если фьютекс был успешно разблокирован. .TP \fBFUTEX_CMP_REQUEUE_PI\fP Возвращается общее количество ожидающих, которые будятся или перемещаются в очередь фьютекса, у которого слово фьютекса задано по адресу \fIuaddr2\fP. Если это значение больше чем \fIval\fP, то разница это количество ожидающих, перемещённых в очередь фьютекса, у которого слово фьютекса задано по адресу \fIuaddr2\fP. .TP \fBFUTEX_WAIT_REQUEUE_PI\fP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Возвращается 0, если вызывающий был успешно перемещён в очередь фьютекса со словом фьютекса по адресу \fIuaddr2\fP. .SH ОШИБКИ .TP \fBEACCES\fP Нет доступа на чтение памяти слова фьютекса. .TP \fBEAGAIN\fP (\fBFUTEX_WAIT\fP, \fBFUTEX_WAIT_BITSET\fP, \fBFUTEX_WAIT_REQUEUE_PI\fP) Значение, на которое указывает \fIuaddr\fP, не было равно ожидаемому значению \fIval\fP на момент вызова. .IP \fBЗамечание\fP: в Linux символические имена \fBEAGAIN\fP и \fBEWOULDBLOCK\fP (оба есть в разных частях кода фьютекса ядра) имеют одинаковое значение. .TP \fBEAGAIN\fP (\fBFUTEX_CMP_REQUEUE\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) Значение, на которое указывает \fIuaddr\fP, не равно ожидаемому значению \fIval3\fP. .TP \fBEAGAIN\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) ID нити владельца фьютекса по адресу \fIuaddr\fP (для \fBFUTEX_CMP_REQUEUE_PI\fP — \fIuaddr2\fP) вскоре закончит работу, но не выполнил очистку внутреннего состояния. Попробуйте ещё раз. .TP \fBEDEADLK\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) Слово фьютекса по адресу \fIuaddr\fP уже заблокировано вызывающим. .TP \fBEDEADLK\fP .\" FIXME . I see that kernel/locking/rtmutex.c uses EDEADLK in some .\" places, and EDEADLOCK in others. On almost all architectures .\" these constants are synonymous. Is there a reason that both .\" names are used? .\" .\" tglx (July 2015): "No. We should probably fix that." .\" (\fBFUTEX_CMP_REQUEUE_PI\fP) Во время перемещения в другую очередь ожидающего PI\-фьютекса со словом фьютекса по адресу \fIuaddr2\fP ядро обнаружило тупиковую блокировку (deadlock). .TP \fBEFAULT\fP Требуемый аргумент указателя (т. е., \fIuaddr\fP, \fIuaddr2\fP или \fItimeout\fP) не указывает на допустимый адрес пользовательского пространства. .TP \fBEINTR\fP A \fBFUTEX_WAIT\fP or \fBFUTEX_WAIT_BITSET\fP operation was interrupted by a signal (see \fBsignal\fP(7)). Before Linux 2.6.22, this error could also be returned for a spurious wakeup; since Linux 2.6.22, this no longer happens. .TP \fBEINVAL\fP Операция в \fIfutex_op\fP является одной из тех, что используют ожидание (timeout), но значение аргумента \fItimeout\fP некорректно (\fItv_sec\fP меньше нуля или \fItv_nsec\fP больше 1000000000). .TP \fBEINVAL\fP The operation specified in \fIfutex_op\fP employs one or both of the pointers \fIuaddr\fP and \fIuaddr2\fP, but one of these does not point to a valid object\[em]that is, the address is not four\-byte\-aligned. .TP \fBEINVAL\fP (\fBFUTEX_WAIT_BITSET\fP, \fBFUTEX_WAKE_BITSET\fP) Битовая маска, указанная в \fIval3\fP, равна нулю. .TP \fBEINVAL\fP (\fBFUTEX_CMP_REQUEUE_PI\fP) Значение \fIuaddr\fP равно \fIuaddr2\fP (т. е., предпринята попытка выполнить перемещение в одну и ту же очередь). .TP \fBEINVAL\fP (\fBFUTEX_FD\fP) В \fIval\fP указан некорректный номер сигнала. .TP \fBEINVAL\fP (\fBFUTEX_WAKE\fP, \fBFUTEX_WAKE_OP\fP, \fBFUTEX_WAKE_BITSET\fP, \fBFUTEX_REQUEUE\fP, \fBFUTEX_CMP_REQUEUE\fP) The kernel detected an inconsistency between the user\-space state at \fIuaddr\fP and the kernel state\[em]that is, it detected a waiter which waits in \fBFUTEX_LOCK_PI\fP or \fBFUTEX_LOCK_PI2\fP on \fIuaddr\fP. .TP \fBEINVAL\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_UNLOCK_PI\fP) Ядро обнаружило противоречие между состояние в пользовательском пространстве по адресу \fIuaddr\fP и состоянием в ядре. Это указывает на поврежденное значение состояния или что ядро обнаружило ожидающего на адресе \fIuaddr\fP, который делает это посредством операции \fBFUTEX_WAIT\fP или \fBFUTEX_WAIT_BITSET\fP. .TP \fBEINVAL\fP .\" From a conversation with Thomas Gleixner (Aug 2015): ### .\" The kernel sees: I have non PI state for a futex you tried to .\" tell me was PI (\fBFUTEX_CMP_REQUEUE_PI\fP) Ядро обнаружило противоречие между состояние в пользовательском пространстве по адресу \fIuaddr2\fP и состоянием в ядре, то есть обнаружило, что ожидающий ждёт посредством операции \fBFUTEX_WAIT\fP или \fBFUTEX_WAIT_BITSET\fP на адресе \fIuaddr2\fP. .TP \fBEINVAL\fP (\fBFUTEX_CMP_REQUEUE_PI\fP) Ядро обнаружило противоречие между состояние в пользовательском пространстве по адресу \fIuaddr\fP и состоянием в ядре, то есть обнаружило, что ожидающий ждёт посредством операции \fBFUTEX_WAIT\fP или \fBFUTEX_WAIT_BITSET\fP на адресе \fIuaddr\fP. .TP \fBEINVAL\fP (\fBFUTEX_CMP_REQUEUE_PI\fP) The kernel detected an inconsistency between the user\-space state at \fIuaddr\fP and the kernel state; that is, the kernel detected a waiter which waits on \fIuaddr\fP via \fBFUTEX_LOCK_PI\fP or \fBFUTEX_LOCK_PI2\fP (instead of \fBFUTEX_WAIT_REQUEUE_PI\fP). .TP \fBEINVAL\fP .\" This deals with the case: .\" wait_requeue_pi(A, B); .\" requeue_pi(A, C); (\fBFUTEX_CMP_REQUEUE_PI\fP) Предпринята попытка выполнить перемещение ожидающего на фьютекс, отличный от указанного в соответствующем вызове \fBFUTEX_WAIT_REQUEUE_PI\fP для этого вызывающего. .TP \fBEINVAL\fP (\fBFUTEX_CMP_REQUEUE_PI\fP) Значение аргумента \fIval\fP не равно 1. .TP \fBEINVAL\fP Неверный аргумент. .TP \fBENFILE\fP (\fBFUTEX_FD\fP) Достигнуто ограничение на общее количество открытых файлов в системе. .TP \fBENOMEM\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) Ядро не может выделить память для хранения информации о состоянии. .TP \fBENOSYS\fP В \fIfutex_op\fP задана неверная операция. .TP \fBENOSYS\fP The \fBFUTEX_CLOCK_REALTIME\fP option was specified in \fIfutex_op\fP, but the accompanying operation was neither \fBFUTEX_WAIT\fP, \fBFUTEX_WAIT_BITSET\fP, \fBFUTEX_WAIT_REQUEUE_PI\fP, nor \fBFUTEX_LOCK_PI2\fP. .TP \fBENOSYS\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_UNLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP, \fBFUTEX_WAIT_REQUEUE_PI\fP) Проверка во время выполнения обнаружила, что операция недоступна. Операции с PI\-фьютексами реализованы не на всех архитектурах и не поддерживаются на некоторых моделях ЦП. .TP \fBEPERM\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) Вызывающему запрещено самостоятельно присоединяться к фьютексу по адресу \fIuaddr\fP (для \fBFUTEX_CMP_REQUEUE_PI\fP: фьютексу по адресу \fIuaddr2\fP) (это может быть вызвано повреждением состояния в пользовательском пространстве). .TP \fBEPERM\fP (\fBFUTEX_UNLOCK_PI\fP) Вызывающему не принадлежит блокировка, представленная в слове фьютекса. .TP \fBESRCH\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) Идентификатор нити из слова фьютекса по адресу \fIuaddr\fP не существует. .TP \fBESRCH\fP (\fBFUTEX_CMP_REQUEUE_PI\fP) Идентификатор нити из слова фьютекса по адресу \fIuaddr2\fP не существует. .TP \fBETIMEDOUT\fP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Операция в \fIfutex_op\fP использует время ожидания, указанное в \fItimeout\fP, и время истекло до завершения операции. .SH СТАНДАРТЫ Linux. .SH ИСТОРИЯ Linux 2.6.0. .P Начальная поддержка фьютексов была встроена в Linux 2.5.7, но с другой семантикой, отличающейся от описанной выше. Семантика системного вызова с четырьмя аргументами, описанная в этой странице, появилась в Linux 2.5.40. Пятый аргумент была добавлен в Linux 2.5.70, а шестой аргумент был добавлен в Linux 2.6.7. .SH ПРИМЕРЫ В программе, показанной далее, показано использование фьютексов: родительский и дочерний процессы используют пару фьютексов, расположенных в общем анонимном отображении, для синхронизации доступа к общему ресурсу: терминалу. Каждый из процессов записывает \fInloops\fP (аргумент командной строки, если отсутствует, то 5) сообщений на терминал и использует протокол синхронизации, который гарантирует, что они записываются поочерёдно. Результат работы программы: .P .in +4n .EX $ \fB./futex_demo\fP Родитель (18534) 0 Потомок (18535) 0 Родитель (18534) 1 Потомок (18535) 1 Родитель (18534) 2 Потомок (18535) 2 Родитель (18534) 3 Потомок (18535) 3 Родитель (18534) 4 Потомок (18535) 4 .EE .in .SS "Исходный код программы" .\" SRC BEGIN (futex.c) \& .EX /* futex_demo.c \& Usage: futex_demo [nloops] (Default: 5) \& Demonstrate the use of futexes in a program where parent and child use a pair of futexes located inside a shared anonymous mapping to synchronize access to a shared resource: the terminal. The two processes each write \[aq]num\-loops\[aq] messages to the terminal and employ a synchronization protocol that ensures that they alternate in writing messages. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include \& static uint32_t *futex1, *futex2, *iaddr; \& static int futex(uint32_t *uaddr, int futex_op, uint32_t val, const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3) { return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); } \& /* Acquire the futex pointed to by \[aq]futexp\[aq]: wait for its value to become 1, and then set the value to 0. */ \& static void fwait(uint32_t *futexp) { long s; const uint32_t one = 1; \& /* atomic_compare_exchange_strong(ptr, oldval, newval) atomically performs the equivalent of: \& if (*ptr == *oldval) *ptr = newval; \& It returns true if the test yielded true and *ptr was updated. */ \& while (1) { \& /* Is the futex available? */ if (atomic_compare_exchange_strong(futexp, &one, 0)) break; /* Yes */ \& /* Futex is not available; wait. */ \& s = futex(futexp, FUTEX_WAIT, 0, NULL, NULL, 0); if (s == \-1 && errno != EAGAIN) err(EXIT_FAILURE, "futex\-FUTEX_WAIT"); } } \& /* Release the futex pointed to by \[aq]futexp\[aq]: if the futex currently has the value 0, set its value to 1 and then wake any futex waiters, so that if the peer is blocked in fwait(), it can proceed. */ \& static void fpost(uint32_t *futexp) { long s; const uint32_t zero = 0; \& /* atomic_compare_exchange_strong() was described in comments above. */ \& if (atomic_compare_exchange_strong(futexp, &zero, 1)) { s = futex(futexp, FUTEX_WAKE, 1, NULL, NULL, 0); if (s == \-1) err(EXIT_FAILURE, "futex\-FUTEX_WAKE"); } } \& int main(int argc, char *argv[]) { pid_t childPid; unsigned int nloops; \& setbuf(stdout, NULL); \& nloops = (argc > 1) ? atoi(argv[1]) : 5; \& /* Create a shared anonymous mapping that will hold the futexes. Since the futexes are being shared between processes, we subsequently use the "shared" futex operations (i.e., not the ones suffixed "_PRIVATE"). */ \& iaddr = mmap(NULL, sizeof(*iaddr) * 2, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, \-1, 0); if (iaddr == MAP_FAILED) err(EXIT_FAILURE, "mmap"); \& futex1 = &iaddr[0]; futex2 = &iaddr[1]; \& *futex1 = 0; /* State: unavailable */ *futex2 = 1; /* State: available */ \& /* Create a child process that inherits the shared anonymous mapping. */ \& childPid = fork(); if (childPid == \-1) err(EXIT_FAILURE, "fork"); \& if (childPid == 0) { /* Child */ for (unsigned int j = 0; j < nloops; j++) { fwait(futex1); printf("Child (%jd) %u\en", (intmax_t) getpid(), j); fpost(futex2); } \& exit(EXIT_SUCCESS); } \& /* Parent falls through to here. */ \& for (unsigned int j = 0; j < nloops; j++) { fwait(futex2); printf("Parent (%jd) %u\en", (intmax_t) getpid(), j); fpost(futex1); } \& wait(NULL); \& exit(EXIT_SUCCESS); } .EE .\" SRC END .SH "СМ. ТАКЖЕ" .ad l \fBget_robust_list\fP(2), \fBrestart_syscall\fP(2), \fBpthread_mutexattr_getprotocol\fP(3), \fBfutex\fP(7), \fBsched\fP(7) .P Файлы из дерева исходного кода ядра: .IP \[bu] 3 \fIDocumentation/pi\-futex.txt\fP .IP \[bu] \fIDocumentation/futex\-requeue\-pi.txt\fP .IP \[bu] \fIDocumentation/locking/rt\-mutex.txt\fP .IP \[bu] \fIDocumentation/locking/rt\-mutex\-design.txt\fP .IP \[bu] \fIDocumentation/robust\-futex\-ABI.txt\fP .P Franke, H., Russell, R., and Kirwood, M., 2002. \fIFuss, Futexes and Furwocks: Fast Userlevel Locking in Linux\fP (доклад на симпозиуме по Linux в 2002 году), .br .UR http://kernel.org\:/doc\:/ols\:/2002\:/ols2002\-pages\-479\-495.pdf .UE .P Hart, D., 2009. \fIA futex overview and update\fP, .UR http://lwn.net/Articles/360699/ .UE .P Hart, D.\& and Guniguntala, D., 2009. \fIRequeue\-PI: Making glibc Condvars PI\-Aware\fP (from proceedings of the 2009 Real\-Time Linux Workshop), .UR http://lwn.net/images/conf/rtlws11/papers/proc/p10.pdf .UE .P Drepper, U., 2011. \fIFutexes Are Tricky\fP, .UR http://www.akkadia.org/drepper/futex.pdf .UE .P Пример библиотеки futex, futex\-*.tar.bz2, доступен на .br .UR https://mirrors.kernel.org\:/pub\:/linux\:/kernel\:/people\:/rusty/ .UE .\" .\" FIXME(Torvald) We should probably refer to the glibc code here, in .\" particular the glibc-internal futex wrapper functions that are .\" WIP, and the generic pthread_mutex_t and perhaps condvar .\" implementations. .PP .SH ПЕРЕВОД Русский перевод этой страницы руководства был сделан Azamat Hackimov , Dmitry Bolkhovskikh , Yuri Kozlov и Иван Павлов . .PP Этот перевод является бесплатной документацией; прочитайте .UR https://www.gnu.org/licenses/gpl-3.0.html Стандартную общественную лицензию GNU версии 3 .UE или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ. .PP Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на .MT man-pages-ru-talks@lists.sourceforge.net .ME .