Man page - futex(2)

Packages contains this manual

Available languages:

en fr ja ru

Manual

futex

NOM
BIBLIOTHÈQUE
SYNOPSIS
DESCRIPTION
Argument
Opérations futex
Futex et héritage de priorité
VALEUR RENVOYÉE
ERREURS
STANDARDS
HISTORIQUE
EXEMPLES
Source du programme
VOIR AUSSI
TRADUCTION

NOM

futex – Verrouillage rapide en mode utilisateur

BIBLIOTHÈQUE

BibliothĂšque C standard ( libc , -lc )

SYNOPSIS

#include <linux/futex.h> /* Définition des constantes FUTEX_* */
#include <sys/syscall.h>
/* Définition des constantes SYS_* */
#include <unistd.h>

long syscall(SYS_futex, uint32_t * uaddr , int futex_op , uint32_t val ,
const struct timespec *
timeout , /* ou : uint32_t val2 */
uint32_t *
uaddr2 , uint32_t val3 );

Remarque : la glibc ne fournit pas de fonction autour de futex (), nĂ©cessitant l’utilisation de syscall (2).

DESCRIPTION

L’appel systĂšme futex () offre une mĂ©thode pour attendre qu’une condition soit vraie. On l’utilise en gĂ©nĂ©ral comme construction de blocage dans le contexte de la synchronisation de la mĂ©moire partagĂ©e. Quand on utilise des futex, la majoritĂ© des opĂ©rations de synchronisation s’effectue dans l’espace utilisateur. Un programme de l’espace utilisateur n’utilise l’appel systĂšme futex () que lorsqu’il est probable qu’il doive se bloquer plus longtemps avant que la condition ne soit vraie. D’autres opĂ©rations futex () peuvent ĂȘtre utilisĂ©es pour rĂ©veiller des processus ou des threads qui attendent une condition en particulier.

Un futex est une valeur 32 bits — dĂ©signĂ©e ci-dessous comme « mot futex » —dont l’adresse est fournie Ă  l’appel systĂšme futex () (les futex ont une taille de 32 bits sur toutes les plateformes, y compris les systĂšmes 64 bits). Toutes les opĂ©rations futex sont pilotĂ©es par cette valeur. Afin de partager un futex entre des processus, le futex est placĂ© dans une zone de la mĂ©moire partagĂ©e créée en utilisant (par exemple) mmap (2) ou shmat (2) (ainsi, le mot futex peut avoir plusieurs adresses virtuelles dans diffĂ©rents processus, mais ces adresses se rapportent toutes au mĂȘme emplacement de la mĂ©moire physique). Dans un programme multithreadĂ©, il suffit de mettre le mot futex dans une variable globale partagĂ©e par tous les threads.

Lors de l’exĂ©cution d’une opĂ©ration futex qui demande le blocage d’un thread, le noyau ne le bloquera que si le mot futex a une valeur fournie par le thread appelant (en tant qu’un des paramĂštres de l’appel futex ()) correspondant Ă  celle prĂ©vue du mot futex. Le chargement de la valeur du mot futex, la comparaison de cette valeur avec celle attendue et le blocage s’effectueront de maniĂšre atomique et seront entiĂšrement organisĂ©s par rapport aux opĂ©rations qui sont effectuĂ©es en parallĂšle par d’autres threads sur le mĂȘme mot futex. Ainsi, le mot futex est utilisĂ© pour relier la synchronisation de l’espace utilisateur et l’implĂ©mentation du blocage par le noyau. Tout comme une opĂ©ration compare-and-exchange atomique qui modifie potentiellement la mĂ©moire partagĂ©e, le blocage par futex est une opĂ©ration compare-and-block atomique.

Une utilisation des futex consiste Ă  implĂ©menter des verrous. L’état du verrou (c’est-Ă -dire acquis ou non acquis) peut se reprĂ©senter comme un drapeau auquel on a un accĂšs atomique en mĂ©moire partagĂ©e. En absence de conflit (uncontended case), un thread peut accĂ©der et modifier l’état du verrou avec des instructions atomiques, par exemple le passer de maniĂšre atomique de l’état non acquis Ă  acquis, en utilisant une instruction compare-and-exchange atomique (de telles instructions s’effectuent entiĂšrement dans l’espace utilisateur et le noyau ne conserve aucune information sur l’état du verrou). D’un autre cĂŽtĂ©, un thread peut ĂȘtre incapable d’acquĂ©rir un verrou parce qu’il est dĂ©jĂ  acquis par un autre thread. Il peut alors passer l’attribut du verrou en tant que mot futex, et la valeur reprĂ©sentant l’état acquis en tant que valeur attendue pour l’opĂ©ration d’attente de futex (). Cette opĂ©ration futex () bloquera si et seulement si le verrou est encore acquis (c’est-Ă -dire si la valeur du mot futex correspond toujours Ă  « l’état acquis »). Lorsque le verrou est relĂąchĂ©, le thread doit d’abord rĂ©initialiser l’état du verrou sur non acquis puis exĂ©cuter une opĂ©ration futex qui rĂ©veille les threads bloquĂ©s par le drapeau de verrou utilisĂ© en tant que mot futex (cela peut ĂȘtre mieux optimisĂ© pour Ă©viter les rĂ©veils inutiles). Voir futex (7) pour plus de dĂ©tails sur la maniĂšre d’utiliser les futex.

Outre la fonctionnalitĂ© de base du futex consistant Ă  attendre et Ă  rĂ©veiller, d’autres opĂ©rations futex visent Ă  gĂ©rer des cas d’utilisation plus complexes.

Remarquez qu’aucune initialisation ou destruction explicite n’est nĂ©cessaire pour utiliser les futex ; le noyau ne garde un futex (c’est-Ă -dire un artefact d’implĂ©mentation interne au noyau) que pendant que les opĂ©rations telles que FUTEX_WAIT , dĂ©crite ci-dessous, s’effectuent sur un mot futex en particulier.

Argument

Le paramĂštre uaddr pointe vers un mot futex. Sur toutes les plateformes, les futex sont des entiers de quatre octets qui doivent ĂȘtre alignĂ©s sur une limite de quatre octets. L’opĂ©ration Ă  effectuer sur le futex est indiquĂ©e dans le paramĂštre de futex_op ; val est une valeur dont la signification et l’objectif dĂ©pendent de futex_op .

Les autres paramĂštres ( timeout , uaddr2 et val3 ) ne sont nĂ©cessaires que pour certaines opĂ©rations futex dĂ©crites ci-dessous. Si un de ces arguments n’est pas nĂ©cessaire, il est ignorĂ©.

Pour plusieurs opĂ©rations de blocage, le paramĂštre timeout est un pointeur vers une structure timespec qui indique la durĂ©e maximale de l’opĂ©ration. Toutefois, contrairement au prototype dĂ©crit ci-dessus, pour certaines opĂ©rations, les quatre octets les moins significatifs de ce paramĂštre sont utilisĂ©s comme un entier dont la signification est dĂ©terminĂ©e par l’opĂ©ration. Pour ces opĂ©rations, le noyau diffuse la valeur timeout d’abord Ă  unsigned long , puis Ă  uint32_t , et dans le reste de cette page, ce paramĂštre est dĂ©signĂ© par val2 quand il est interprĂ©tĂ© de cette maniĂšre.

Lorsqu’il est nĂ©cessaire, le paramĂštre uaddr2 est un pointeur vers un deuxiĂšme mot futex utilisĂ© par l’opĂ©ration.

L’interprĂ©tation du paramĂštre de l’entier final, val3 , dĂ©pend de l’opĂ©ration.

Opérations futex

Le paramĂštre futex_op est en deux parties : une commande qui indique l’opĂ©ration Ă  effectuer et un bit ORed avec zĂ©ro ou plusieurs options qui changent le comportement de l’opĂ©ration. Les options qui peuvent ĂȘtre incluses dans futex_op sont les suivantes :
FUTEX_PRIVATE_FLAG
(depuis Linux 2.6.22)

Ce bit d’option peut ĂȘtre utilisĂ© avec toutes les opĂ©rations futex. Il dit au noyau que le futex est un processus privĂ© non partagĂ© avec d’autres processus (c’est-Ă -dire qu’il n’est utilisĂ© que pour la synchronisation entre les threads du mĂȘme processus). Cela permet au noyau d’effectuer des optimisations de performance supplĂ©mentaires.

Par commoditĂ©, <linux/futex.h> dĂ©finit un ensemble de constantes dont le suffixe est _PRIVATE et qui sont Ă©quivalentes Ă  toutes les opĂ©rations listĂ©es ci-dessous mais avec l’attribut FUTEX_PRIVATE_FLAG ORed dans la valeur de la constante. On trouve ainsi FUTEX_WAIT_PRIVATE , FUTEX_WAKE_PRIVATE et ainsi de suite.

FUTEX_CLOCK_REALTIME (depuis Linux 2.6.28)

Ce bit d’option ne peut ĂȘtre utilisĂ© qu’avec les opĂ©rations FUTEX_WAIT_BITSET , FUTEX_WAIT_REQUEUE_PI (depuis Linux 4.5), FUTEX_WAIT (depuis Linux 4.5) et FUTEX_LOCK_PI2 (depuis Linux 5.14).

Si cette option est positionnĂ©e, le noyau mesure le timeout par rapport Ă  l’horloge CLOCK_REALTIME .

Si cette option n’est pas positionnĂ©e, le noyau mesure le timeout par rapport Ă  l’horloge CLOCK_MONOTONIC .

L’opĂ©ration indiquĂ©e dans futex_op prend une de ces valeurs :
FUTEX_WAIT
(depuis Linux 2.6.0)

Cette option teste que la valeur du mot futex vers laquelle pointe l’adresse uaddr contient toujours la valeur val attendue, et si tel est le cas, elle s’endort jusqu’à une opĂ©ration FUTEX_WAKE sur le mot futex. Le chargement de la valeur du mot futex est un accĂšs en mĂ©moire atomique (c’est-Ă -dire qu’il utilise des instructions machine atomiques de l’architecture concernĂ©e). Ce chargement, la comparaison avec la valeur attendue et la mise en sommeil s’effectuent de maniĂšre atomique et sont totalement organisĂ©s selon les autres opĂ©rations futex sur le mĂȘme mot futex. Si le thread commence Ă  dormir, il est considĂ©rĂ© comme en attente de ce mot futex. Si la valeur futex ne correspond pas Ă  val , l’appel Ă©choue immĂ©diatement avec l’erreur EAGAIN .

Le but de la comparaison avec la valeur attendue est d’empĂȘcher des rĂ©veils perdus. Si un autre thread a changĂ© la valeur du mot futex aprĂšs que le thread a dĂ©cidĂ© de se bloquer en se fondant sur la valeur d’avant, et si l’autre thread a effectuĂ© une opĂ©ration FUTEX_WAKE (ou un rĂ©veil Ă©quivalent) aprĂšs le changement de cette valeur et avant cette opĂ©ration FUTEX_WAIT , le thread appelant observera cette valeur et ne commencera pas Ă  dormir.

Si le timeout n’est pas NULL, la structure vers laquelle il pointe indique un dĂ©lai d’attente (cet intervalle sera arrondi Ă  la valeur supĂ©rieure Ă  partir de la granularitĂ© de l’horloge systĂšme et il est garanti de ne pas expirer en avance). Le dĂ©lai est mesurĂ© par dĂ©faut par rapport Ă  l’horloge CLOCK_MONOTONIC mais depuis Linux 4.5, l’horloge CLOCK_REALTIME peut ĂȘtre choisie en indiquant FUTEX_CLOCK_REALTIME dans futex_op . Si le timeout est NULL, l’appel se bloque indĂ©finiment.

Remarque : pour FUTEX_WAIT , le timeout est interprĂ©tĂ© comme une valeur relative. Cela diffĂšre des autres opĂ©rations futex oĂč le timeout est interprĂ©tĂ© comme une valeur absolue. Pour obtenir l’équivalent de FUTEX_WAIT , avec un dĂ©lai absolu, utilisez FUTEX_WAIT_BITSET en indiquant val3 comme FUTEX_BITSET_MATCH_ANY .

Les paramÚtres uaddr2 et val3 sont ignorés.

FUTEX_WAKE (depuis Linux 2.6.0)

Cette opĂ©ration rĂ©veille jusqu’à val Ă©lĂ©ments en attente (comme dans FUTEX_WAIT ) sur le mot futex Ă  l’adresse uaddr . GĂ©nĂ©ralement, val est indiquĂ© soit sous la forme de 1 (rĂ©veil d’un seul Ă©lĂ©ment en attente) soit avec INT_MAX (rĂ©veil de tous les Ă©lĂ©ments en attente). Vous n’avez aucune garantie quant aux Ă©lĂ©ments qui sont rĂ©veillĂ©s (par exemple un Ă©lĂ©ment en attente dont la prioritĂ© d’ordonnancement Ă©levĂ©e n’est pas garanti de se rĂ©veiller avant un autre d’une prioritĂ© plus basse).

Les paramÚtres timeout , uaddr2 et val3 sont ignorés.

FUTEX_FD (de Linux 2.6.0 jusqu’à Linux 2.6.25 inclus)

Cette opĂ©ration crĂ©e un descripteur de fichier associĂ© au futex sur uaddr . L’appelant doit fermer le descripteur de fichier renvoyĂ© aprĂšs l’avoir utilisĂ©. Quand un autre processus ou un autre thread effectue un FUTEX_WAKE sur le mot futex, le descripteur de fichier indique qu’il est accessible en lecture avec select (2), poll (2), et epoll (7)

Le descripteur de fichier peut ĂȘtre utilisĂ© pour avoir des notifications asynchrones, si val n’est pas nul, puis, quand un autre processus ou un autre thread exĂ©cute FUTEX_WAKE , l’appelant recevra le numĂ©ro du signal passĂ© Ă  val .

Les paramÚtres timeout , uaddr2 et val3 sont ignorés.

Parce qu’il Ă©tait de façon inhĂ©rente sujet Ă  des situations de concurrence, FUTEX_FD a Ă©tĂ© supprimĂ© de Linux 2.6.26 et les suivants.

FUTEX_REQUEUE (depuis Linux 2.6.0)

Cette opĂ©ration effectue la mĂȘme chose que FUTEX_CMP_REQUEUE (voir ci-dessous), sauf qu’elle ne vĂ©rifie rien en utilisant la valeur dans val3 (le paramĂštre val3 est ignorĂ©).

FUTEX_CMP_REQUEUE (depuis Linux 2.6.7)

Cette opĂ©ration vĂ©rifie d’abord si l’emplacement uaddr contient toujours la valeur val3 . Si tel n’est pas le cas, l’opĂ©ration Ă©choue avec l’erreur EAGAIN . Si tel est le cas, l’opĂ©ration rĂ©veille un maximum de val Ă©lĂ©ments en attente du futex sur uaddr . S’il y a plus de val Ă©lĂ©ments en attente, les autres sont supprimĂ©s de la file d’attente du futex source sur uaddr et ajoutĂ©s Ă  la file d’attente du futex cible sur uaddr2 . Le paramĂštre val2 indique une limite supĂ©rieure du nombre d’élĂ©ments remis en attente dans le futex sur uaddr2 .

Le chargement Ă  partir de uaddr est un accĂšs atomique en mĂ©moire (c’est-Ă -dire qu’il utilise les instructions machine atomiques de l’architecture concernĂ©e). Ce chargement, la comparaison avec val3 et la remise en attente d’élĂ©ments s’effectuent de maniĂšre atomique et sont totalement organisĂ©es par rapport aux autres opĂ©rations sur le mĂȘme mot futex.

Les valeurs classiques qu’on indique Ă  val sont 0 ou 1 (indiquer INT_MAX n’est pas utile car cela rendrait l’opĂ©ration FUTEX_CMP_REQUEUE Ă©quivalente Ă  FUTEX_WAKE ). La valeur limite indiquĂ©e avec val2 est gĂ©nĂ©ralement 1 ou INT_MAX (indiquer 0 en paramĂštre n’est pas utile car cela rendrait l’opĂ©ration FUTEX_CMP_REQUEUE Ă©quivalente Ă  FUTEX_WAIT ).

L’opĂ©ration FUTEX_CMP_REQUEUE a Ă©tĂ© ajoutĂ©e pour remplacer l’ancienne FUTEX_REQUEUE . La diffĂ©rence est que la vĂ©rification de la valeur sur uaddr peut ĂȘtre utilisĂ©e pour s’assurer que la remise en attente ne se produit que sous certaines conditions, ce qui Ă©vite les conflits de mĂ©moire (race conditions) dans certains cas d’utilisation.

FUTEX_REQUEUE et FUTEX_CMP_REQUEUE peuvent ĂȘtre utilisĂ©es pour Ă©viter des rĂ©veils en troupeau (thundering herd) qui peuvent survenir quand on utilise FUTEX_WAKE dans des cas oĂč tous les Ă©lĂ©ments en attente qu’on rĂ©veille doivent acquĂ©rir un autre futex. Imaginons le scĂ©nario suivant oĂč plusieurs threads attendent en B, une file d’attente implĂ©mentĂ©e en utilisant un futex :

lock(A)
while (!check_value(V)) {
unlock(A);
block_on(B);
lock(A);
};
unlock(A);

Si un thread qui se rĂ©veille utilisait FUTEX_WAKE , tous les Ă©lĂ©ments attendant en B se rĂ©veilleraient et essaieraient d’acquĂ©rir le verrou A. Cependant, rĂ©veiller tous ces threads de cette maniĂšre serait vain car tous les threads, sauf un, se bloqueraient immĂ©diatement Ă  nouveau via le verrou A. Au contraire, une remise dans la file d’attente ne rĂ©veille qu’un Ă©lĂ©ment et dĂ©place les autres sur le verrou A et quand celui rĂ©veillĂ© dĂ©verrouille A, le suivant peut continuer.

FUTEX_WAKE_OP (depuis Linux 2.6.14)

Cette opĂ©ration a Ă©tĂ© ajoutĂ©e pour prendre en charge certains cas d’utilisation de l’espace utilisateur oĂč plus d’un futex Ă  la fois doit ĂȘtre gĂ©rĂ©. L’exemple le plus frappant est l’implĂ©mentation de pthread_cond_signal (3), qui nĂ©cessite des opĂ©rations sur deux futex, une pour implĂ©menter le mutex, l’autre pour utiliser dans l’implĂ©mentation de la file d’attente associĂ©e Ă  la variable conditionnelle. FUTEX_WAKE_OP permet d’implĂ©menter de tels cas sans augmenter le nombre de conflits et de changement de contexte.

L’opĂ©ration FUTEX_ WAKE_OP revient Ă  exĂ©cuter le code suivant de maniĂšre atomique et complĂštement organisĂ© en fonction des opĂ©rations futex sur un des deux mots futex fournis :

uint32_t oldval = *(uint32_t *) uaddr2;
*(uint32_t *) uaddr2 = oldval op oparg ;
futex(uaddr, FUTEX_WAKE, val, 0, 0, 0);
if (oldval cmp cmparg )
futex(uaddr2, FUTEX_WAKE, val2, 0, 0, 0);

En d’autres termes, FUTEX_WAKE_OP fait ce qui suit :

-

sauvegarde la valeur d’origine du mot futex sur uaddr2 et effectue une opĂ©ration pour modifier la valeur du futex sur uaddr2 ; il s’agit d’un accĂšs en mĂ©moire read-modify-write atomique (c’est-Ă -dire d’une utilisation des instructions machine atomiques liĂ©es Ă  l’architecture concernĂ©e)

-

réveille un maximum de val éléments en attente sur le futex pour le mot futex sur uaddr ;

-

et selon les rĂ©sultats d’un test de la valeur d’origine du mot futex sur uaddr2 , rĂ©veille un maximum de val2 Ă©lĂ©ments en attente du mot futex sur le futex sur uaddr2 .

L’opĂ©ration et la comparaison qui doivent ĂȘtre effectuĂ©es sont encodĂ©es dans les bits du paramĂštre val3 . Visuellement, l’encodage est :

+---+---+-----------+-----------+
|op |cmp| oparg | cmparg |
+---+---+-----------+-----------+
4 4 12 12 <== # of bits

ExprimĂ© en code, l’encodage est :

#define FUTEX_OP(op, oparg, cmp, cmparg) \
(((op & 0xf) << 28) | \
((cmp & 0xf) << 24) | \
((oparg & 0xfff) << 12) | \
(cmparg & 0xfff))

Dans ce qui précÚde, op et cmp sont chacun des codes listés ci-dessous. Les composants oparg et cmparg sont des valeurs numériques littérales, sauf les remarques ci-dessous.

Le composant op prend une de ces valeurs :

FUTEX_OP_SET 0 /* uaddr2 = oparg; */
FUTEX_OP_ADD 1 /* uaddr2 += oparg; */
FUTEX_OP_OR 2 /* uaddr2 |= oparg; */
FUTEX_OP_ANDN 3 /* uaddr2 &= ~oparg; */
FUTEX_OP_XOR 4 /* uaddr2 ^= oparg; */

En outre, comparer bit Ă  bit (ORing) la valeur suivante dans op a pour consĂ©quence que (1 << oparg) sera utilisĂ© en tant qu’opĂ©rande :

FUTEX_OP_ARG_SHIFT 8 /* Utiliser (1 << oparg) comme opérande */

Le champ cmp prend une de ces valeurs :

FUTEX_OP_CMP_EQ 0 /* si (oldval == cmparg) réveiller */
FUTEX_OP_CMP_NE 1 /* si (oldval != cmparg) réveiller */
FUTEX_OP_CMP_LT 2 /* si (oldval < cmparg) réveiller */
FUTEX_OP_CMP_LE 3 /* si (oldval <= cmparg) réveiller */
FUTEX_OP_CMP_GT 4 /* si (oldval > cmparg) réveiller */
FUTEX_OP_CMP_GE 5 /* si (oldval >= cmparg) réveiller */

Le code de retour de FUTEX_WAKE_OP est la somme du nombre d’élĂ©ments en attente rĂ©veillĂ©s par le futex uaddr et du nombre d’élĂ©ments en attente rĂ©veillĂ©s sur le futex uaddr2 .

FUTEX_WAIT_BITSET (depuis Linux 2.6.25)

Cette opĂ©ration est Ă©quivalente Ă  FUTEX_WAIT , sauf que val3 est utilisĂ© pour fournir un masque de bit de 32 bits au noyau. Ce masque, oĂč au moins un bit doit ĂȘtre positionnĂ©, est stockĂ© dans la partie interne du noyau de l’élĂ©ment en attente. Voir la description de FUTEX_WAKE_BITSET pour plus de dĂ©tails.

Si timeout n’est pas NULL, la structure vers laquelle il pointe indique un dĂ©lai absolu de l’opĂ©ration d’attente. Si timeout est NULL, l’opĂ©ration peut se bloquer indĂ©finiment.

L’argument uaddr2 est ignorĂ©.

FUTEX_WAKE_BITSET (depuis Linux 2.6.25)

Cette opĂ©ration est identique Ă  FUTEX_WAKE , sauf que le paramĂštre val3 est utilisĂ© pour fournir un masque de bit de 32 bits au noyau. Ce masque, oĂč au moins un bit doit ĂȘtre positionnĂ©, est utilisĂ© pour choisir les Ă©lĂ©ments en attente qui doivent ĂȘtre rĂ©veillĂ©s. Le choix se fait par une comparaison bit Ă  bit AND du masque de bit « wait » (Ă  savoir la valeur de val3 ) et par un masque de bit stockĂ© dans la partie interne de l’élĂ©ment en attente (le masque de bit « wait » positionnĂ© en utilisant FUTEX_WAIT_BITSET ). Tous les Ă©lĂ©ments en attente pour lesquels le AND est positif sont rĂ©veillĂ©s ; les autres restent endormis.

L’effet de FUTEX_WAIT_BITSET et de FUTEX_WAKE_BITSET est de permettre un rĂ©veil sĂ©lectif parmi les Ă©lĂ©ments en attente bloquĂ©s sur le mĂȘme futex. Cependant, remarquez que selon le cas, l’utilisation de cette fonction de mĂ©lange de masques de bit sur un futex peut ĂȘtre moins efficace que le fait d’avoir plusieurs futex, car elle a besoin que le noyau vĂ©rifie tous les Ă©lĂ©ments en attente sur un futex, y compris ceux non concernĂ©s par le rĂ©veil (Ă  savoir qu’ils n’ont pas de bit pertinent positionnĂ© dans leur masque de bit « wait »).

La constante FUTEX_BITSET_MATCH_ANY , qui correspond Ă  tous les positionnements 32 bits du masque, peut ĂȘtre utilisĂ© en tant que val3 de FUTEX_WAIT_BITSET et de FUTEX_WAKE_BITSET . En dehors des diffĂ©rences dans la gestion du paramĂštre timeout , l’opĂ©ration FUTEX_WAIT est Ă©quivalente Ă  FUTEX_WAIT_BITSET oĂč val3 est indiquĂ© en tant que FUTEX_BITSET_MATCH_ANY ; c’est-Ă -dire permettre le rĂ©veil par n’importe quel Ă©lĂ©ment en attente). L’opĂ©ration FUTEX_WAKE est Ă©quivalente Ă  FUTEX_WAKE_BITSET oĂč val3 est indiquĂ© en tant que FUTEX_BITSET_MATCH_ANY ; c’est-Ă -dire, rĂ©veiller n’importe quel Ă©lĂ©ment en attente.

Les arguments uaddr2 et timeout sont ignorés.

Futex et héritage de priorité

Linux prend en charge l’hĂ©ritage de prioritĂ© (priority inheritance, PI) des futex, afin de gĂ©rer des problĂšmes d’inversion des prioritĂ©s qu’on peut rencontrer avec des verrous futex normaux. L’inversion des prioritĂ©s est un problĂšme qui survient quand une tĂąche de haute prioritĂ© est bloquĂ©e en attente d’acquĂ©rir un verrou que possĂšde une tĂąche de basse prioritĂ© issue du processeur. Du coup, la tĂąche de prioritĂ© basse ne va pas relĂącher le verrou et celle de haute prioritĂ© reste bloquĂ©e.

L’hĂ©ritage de prioritĂ© est un mĂ©canisme pour gĂ©rer le problĂšme d’inversion des prioritĂ©s. Avec ce mĂ©canisme, quand une tĂąche Ă  haute prioritĂ© est bloquĂ©e par un verrou possĂ©dĂ© par une tĂąche Ă  basse prioritĂ©, la prioritĂ© de la seconde est temporairement amenĂ©e au mĂȘme niveau que celle Ă  haute prioritĂ©, de sorte qu’elle ne soit pas doublĂ©e par une tĂąche de niveau intermĂ©diaire et qu’elle puisse ainsi avancer pour relĂącher le verrou. Pour fonctionner, l’hĂ©ritage de prioritĂ© doit ĂȘtre transitif, ce qui signifie que si une tĂąche Ă  haute prioritĂ© bloque sur le verrou d’une tĂąche Ă  prioritĂ© intermĂ©diaire (et ainsi de suite sur des chaĂźnes de la taille de votre choix), les deux tĂąches (ou plus gĂ©nĂ©ralement toutes les tĂąches de la chaĂźne de verrous) voient leur niveau de prioritĂ© amenĂ© Ă  celui de la tĂąche Ă  haute prioritĂ©.

Du point de vue de l’espace utilisateur, le futex a conscience d’un PI en acceptant une rĂ©glementation (dĂ©crite ci-dessous) entre l’espace utilisateur et le noyau sur la valeur du mot futex, couplĂ© Ă  l’utilisation d’opĂ©rations futex PI dĂ©crites ci-dessous (contrairement aux autres opĂ©rations futex dĂ©crites ci-dessus, celles PI-futex sont conçues pour l’implĂ©mentation de mĂ©canismes IPC trĂšs spĂ©cifiques).

Les opĂ©rations PI-futex dĂ©crites ci-dessous diffĂšrent des autres opĂ©rations dans le sens oĂč elles imposent des rĂšgles dans l’utilisation de la valeur du mot futex :

-

Si le verrou n’est pas acquis, la valeur du mot futex doit ĂȘtre 0 .

-

Si le verrou est acquis, la valeur du mot futex doit ĂȘtre l’ID du thread (TID ; voir gettid (2)) du thread propriĂ©taire.

-

Si le verrou a un propriĂ©taire et s’il y a des threads en concurrence pour le verrou, le bit FUTEX_WAITERS doit ĂȘtre positionnĂ© dans la valeur du mot futex ; autrement dit, cette valeur est :

FUTEX_WAITERS | TID

(Remarquez que cela n’est pas possible pour un mot futex PI d’ĂȘtre sans propriĂ©taire ni FUTEX_WAITERS dĂ©fini).

Avec cette rĂšgle, une application de l’espace utilisateur peut acquĂ©rir un verrou non acquis ou en relĂącher un en utilisant des instructions atomiques dans l’espace utilisateur (comme une opĂ©ration compare-and-swap telle que cmpxchg sur l’architecture x86). L’acquisition d’un verrou consiste simplement dans l’utilisation de compare-and-swap pour positionner la valeur du mot futex de maniĂšre atomique sur le TID de l’appelant si sa valeur prĂ©cĂ©dente Ă©tait 0 . RelĂącher un verrou exige d’utiliser compare-and-swap pour positionner la valeur du mot futex sur 0 si la valeur prĂ©cĂ©dente Ă©tait le TID prĂ©vu.

Si un futex est dĂ©jĂ  acquis (c’est-Ă -dire qu’il a une valeur positive), les Ă©lĂ©ments en attente doivent utiliser l’opĂ©ration FUTEX_LOCK_PI pour acquĂ©rir le verrou. Si d’autres threads attendent le verrou, le bit FUTEX_WAITERS est dĂ©fini dans la valeur du futex ; dans ce cas le dĂ©tenteur du verrou doit utiliser l’opĂ©ration FUTEX_UNLOCK_PI pour relĂącher le verrou.

Dans le cas oĂč les appelants sont bloquĂ©s dans le noyau (c’est-Ă -dire qu’ils doivent effectuer un appel futex ()), ils traitent directement avec ce qu’on appelle un RT-mutex, un mĂ©canisme de verrouillage du noyau qui implĂ©mente la sĂ©mantique de l’hĂ©ritage de prioritĂ© requis. AprĂšs que le RT-mutex est acquis, la valeur futex est mise Ă  jour en fonction, avant que le thread appelant ne renvoie vers l’espace utilisateur.

Il est important de remarquer que le noyau mettra Ă  jour la valeur du mot futex avant de renvoyer vers l’espace utilisateur (cela enlĂšve la possibilitĂ© pour la valeur d’un mot futex de se terminer dans un Ă©tat non valable, par exemple en ayant un propriĂ©taire mais en ayant la valeur 0 , ou en ayant des Ă©lĂ©ments en attente mais aucun bit FUTEX_WAITERS positionnĂ©).

Si un futex a un RT-mutex associĂ© dans le noyau (c’est-Ă -dire qu’il y a des Ă©lĂ©ments en attente bloquĂ©s) et si le propriĂ©taire du futex/RT-mutex meurt de maniĂšre inattendue, le noyau nettoie le RT-mutex et passe la main au prochain Ă©lĂ©ment en attente. Cela implique, en retour, que la valeur dans l’espace utilisateur soit mise Ă  jour en fonction. Pour dire que c’est nĂ©cessaire, le noyau positionne le bit FUTEX_OWNER_DIED dans le mot futex ainsi que dans l’ID du thread du nouveau propriĂ©taire. L’espace utilisateur peut dĂ©tecter cette situation par la prĂ©sence du bit FUTEX_OWNER_DIED et il est alors responsable pour nettoyer l’espace laissĂ© par le propriĂ©taire mort.

Les PI futex sont utilisĂ©s en indiquant une des valeurs listĂ©es ci-dessous dans futex_op . Remarquez que les opĂ©rations de PI futex doivent ĂȘtre utilisĂ©es par paires et sont soumises Ă  des exigences supplĂ©mentaires :

-

FUTEX_LOCK_PI , FUTEX_LOCK_PI2 et FUTEX_TRYLOCK_PI vont de pair avec FUTEX_UNLOCK_PI . FUTEX_UNLOCK_PI ne doit ĂȘtre appelĂ© que sur un futex appartenant au thread appelant, tel que dĂ©fini par les rĂšgles de la valeur, sans quoi on obtient l’erreur EPERM .

-

FUTEX_WAIT_REQUEUE_PI va de pair avec FUTEX_CMP_REQUEUE_PI . Elles doivent s’effectuer depuis un futex non-PI vers un PI futex distinct (sans quoi on obtient l’erreur EINVAL ). De plus, val (le nombre d’élĂ©ments en attente Ă  rĂ©veiller) doit ĂȘtre de 1 (sans quoi on obtient l’erreur EINVAL ).

Les opérations PI futex sont comme suit :
FUTEX_LOCK_PI
(depuis Linux 2.6.18)

Cette opĂ©ration est utilisĂ©e aprĂšs avoir essayĂ© sans succĂšs d’acquĂ©rir un verrou en utilisant une instruction atomique en mode utilisateur, car le mot futex a une valeur positive – en particulier parce qu’il contenait le TID (spĂ©cifique Ă  l’espace de noms PID) du verrou propriĂ©taire.

L’opĂ©ration vĂ©rifie la valeur du mot futex sur l’adresse uaddr . Si la valeur est de 0 , le noyau essaie de positionner de maniĂšre atomique la valeur du futex sur le TID de l’appelant. Si la valeur du mot futex est positive, le noyau positionne de maniĂšre atomique le bit FUTEX_WAITERS , qui signale au propriĂ©taire du futex qu’il ne peut pas dĂ©verrouiller le futex dans l’espace utilisateur de maniĂšre atomique, en positionnant la valeur du futex Ă  0 . AprĂšs cela, le noyau :

(1)

Essaie de trouver le thread associé au TID du propriétaire.

(2)

CrĂ©e ou rĂ©utilise l’état du noyau sur la base du propriĂ©taire (s’il s’agit du premier Ă©lĂ©ment en attente, il n’existe pas d’état du noyau pour ce futex, donc il est créé en verrouillant le RT-mutex et le propriĂ©taire du futex devient propriĂ©taire du RT-mutex). Si des Ă©lĂ©ments en attente existent, l’état existant est rĂ©utilisĂ©.

(3)

Rattache l’élĂ©ment en attente au futex (c’est-Ă -dire que l’élĂ©ment est mis dans la file d’attente du RT-futex).

S’il existe plus d’un Ă©lĂ©ment en attente, la mise dans la file d’un Ă©lĂ©ment se fait par ordre de prioritĂ© descendant (pour des informations sur l’ordre des prioritĂ©s, voir les points sur l’ordonnancement SCHED_DEADLINE , SCHED_FIFO et SCHED_RR dans sched (7)). Le propriĂ©taire hĂ©rite soit de la bande passante de processeur de l’élĂ©ment en attente (si l’élĂ©ment est programmĂ© sous la rĂšgle SCHED_DEADLINE ou SCHED_FIFO ), soit de la prioritĂ© de l’élĂ©ment en attente (s’il est programmĂ© sous la rĂšgle SCHED_RR ou SCHED_FIFO ). Cet hĂ©ritage suit la chaĂźne de verrous dans les cas de verrous imbriquĂ©s et il effectue la dĂ©tection des verrous morts (deadlocks).

Le paramĂštre timeout fournit un dĂ©lai de tentative de verrouillage. Si timeout est positif, la structure vers laquelle il pointe indique un dĂ©lai absolu mesurĂ© en fonction de l’horloge CLOCK_REALTIME . Si timeout est NULL, l’opĂ©ration se bloquera indĂ©finiment.

Les paramÚtres uaddr2 , val et val3 sont ignorés.

FUTEX_LOCK_PI2 (depuis Linux 5.14)

Cette opĂ©ration est la mĂȘme que FUTEX_LOCK_PI , sauf que l’horloge par rapport Ă  laquelle timeout est mesurĂ© peut ĂȘtre sĂ©lectionnĂ©e. Par dĂ©faut, le dĂ©lai (absolu) indiquĂ© dans timeout est mesurĂ© par rapport Ă  l’horloge CLOCK_MONOTONIC mais si l’attribut FUTEX_CLOCK_REALTIME est indiquĂ© dans futex_op , le dĂ©lai est mesurĂ© par rapport Ă  l’horloge CLOCK_REALTIME .

FUTEX_TRYLOCK_PI (depuis Linux 2.6.18)

L’opĂ©ration essaie d’acquĂ©rir le verrou sur uaddr . Elle est appelĂ©e quand l’acquisition atomique dans l’espace utilisateur n’a pas rĂ©ussi parce que le mot futex ne valait pas 0 .

Du fait que le noyau accĂšde Ă  plus d’informations d’état que l’espace utilisateur, l’acquisition du verrou pourrait rĂ©ussir si elle est effectuĂ©e par le noyau dans les cas oĂč le mot futex (c’est-Ă -dire les informations d’état accessibles dans l’espace utilisateur) contient un Ă©tat stable ( FUTEX_WAITERS et/ou FUTEX_OWNER_DIED ). Cela peut arriver quand le propriĂ©taire du futex est mort. L’espace utilisateur ne peut pas gĂ©rer cette condition de maniĂšre "race-free", mais le noyau peut corriger cela et acquĂ©rir le futex.

Les paramÚtres uaddr2 , val , timeout et val3 sont ignorés.

FUTEX_UNLOCK_PI (depuis Linux 2.6.18)

Cette opĂ©ration rĂ©veille l’élĂ©ment ayant la plus haute prioritĂ© et attendant un FUTEX_LOCK_PI ou un FUTEX_LOCK_PI2 Ă  l’adresse indiquĂ©e par le paramĂštre uaddr .

Cela est appelĂ© quand la valeur dans l’espace utilisateur sur uaddr ne peut pas ĂȘtre passĂ©e Ă  0 de maniĂšre atomique depuis un TID (du propriĂ©taire).

Les paramÚtres uaddr2 , val , timeout et val3 sont ignorés.

FUTEX_CMP_REQUEUE_PI (depuis Linux 2.6.31)

Cette opĂ©ration est une variante PI-aware de FUTEX_CMP_REQUEUE . Elle remet en attente des Ă©lĂ©ments bloquĂ©s avec FUTEX_WAIT_REQUEUE_PI sur uaddr Ă  partir d’un futex source non-PI ( uaddr ) vers un futex cible PI ( uaddr2 ).

Comme avec FUTEX_CMP_REQUEUE , cette opĂ©ration rĂ©veille un maximum de val Ă©lĂ©ments qui attendent le futex sur uaddr . Toutefois, pour FUTEX_CMP_REQUEUE_PI , val doit valoir 1 (puisque son but principal est d’éviter l’effet de troupeau (thundering herd). Les autres Ă©lĂ©ments sont supprimĂ©s de la file d’attente du futex source sur uaddr et ajoutĂ©s sur celle du futex cible sur uaddr2 .

Les paramĂštres val2 et val3 ont le mĂȘme objectif qu’avec FUTEX_CMP_REQUEUE .

FUTEX_WAIT_REQUEUE_PI (depuis Linux 2.6.31)

Attendre un futex non-PI sur uaddr et se mettre potentiellement en attente (avec une opĂ©ration FUTEX_CMP_REQUEUE_PI dans une autre tĂąche), d’un futex PI sur uaddr2 . L’opĂ©ration d’attente sur uaddr est la mĂȘme que pour FUTEX_WAIT .

L’élĂ©ment peut ĂȘtre retirĂ© de la file d’attente sur uaddr sans ĂȘtre transfĂ©rĂ© sur uaddr2 Ă  l’aide d’une opĂ©ration FUTEX_WAKE dans une autre tĂąche. Dans ce cas, l’opĂ©ration FUTEX_WAIT_REQUEUE_PI Ă©choue avec l’erreur EAGAIN .

Si timeout n’est pas NULL, la structure vers laquelle il pointe indique un dĂ©lai absolu de l’opĂ©ration d’attente. Si timeout est NULL, l’opĂ©ration peut se bloquer indĂ©finiment.

L’argument val3 est ignorĂ©.

FUTEX_WAIT_REQUEUE_PI et FUTEX_CMP_REQUEUE_PI ont Ă©tĂ© ajoutĂ©s pour gĂ©rer un cas d’utilisation bien particulier : la prise en charge des variables conditionnelles de threads POSIX ayant connaissance de l’hĂ©ritage de prioritĂ©. L’idĂ©e est que ces opĂ©rations devraient toujours aller par paires, afin de garantir que l’espace utilisateur et le noyau restent toujours synchronisĂ©s. Ainsi, dans l’opĂ©ration FUTEX_WAIT_REQUEUE_PI , l’application dans l’espace utilisateur prĂ©-indique la cible de la remise en attente qui va se faire dans l’opĂ©ration FUTEX_CMP_REQUEUE_PI .

VALEUR RENVOYÉE

En cas d’erreur (en supposant que futex () a Ă©tĂ© appelĂ© Ă  l’aide de syscall (2)), toutes les opĂ©rations renvoient -1 et positionnent errno pour indiquer l’erreur.

En cas de succĂšs, le code de retour dĂ©pend de l’opĂ©ration, comme dĂ©crit dans la liste suivante :
FUTEX_WAIT

Renvoie 0 si l’appelant a Ă©tĂ© rĂ©veillĂ©. Remarquez qu’un rĂ©veil peut Ă©galement rĂ©sulter de l’utilisation de motifs d’utilisation classiques de futex dans du code non liĂ© qui a pu utiliser l’emplacement mĂ©moire du mot futex (par exemple des implĂ©mentations classiques basĂ©es sur futex de mutex Pthreads peuvent provoquer cela dans certaines conditions). Donc, les appelants devraient toujours, Ă  titre conservatoire, supposer qu’un code de retour 0 peut signifier un faux rĂ©veil, et donc utiliser la valeur du mot futex (Ă  savoir le schĂ©ma de synchronisation de l’espace utilisateur) pour dĂ©cider de rester bloquĂ©s ou pas.

FUTEX_WAKE

Renvoie le nombre de processus en attente qui ont été réveillés.

FUTEX_FD

Renvoie le nouveau descripteur de fichier associé au futex.

FUTEX_REQUEUE

Renvoie le nombre de processus en attente qui ont été réveillés.

FUTEX_CMP_REQUEUE

Renvoie le nombre total d’élĂ©ments en attente rĂ©veillĂ©s ou remis dans la file du futex pour le mot futex sur uaddr2 . Si cette valeur est supĂ©rieure Ă  val , la diffĂ©rence devient le nombre d’élĂ©ments en attente remis dans la file du futex pour le mot futex sur uaddr2 .

FUTEX_WAKE_OP

Renvoie le nombre total d’élĂ©ments en attente rĂ©veillĂ©s. Il s’agit de la somme des Ă©lĂ©ments rĂ©veillĂ©s sur les deux futex pour les mots futex sur uaddr et uaddr2 .

FUTEX_WAIT_BITSET

Renvoie 0 si l’appelant a Ă©tĂ© rĂ©veillĂ©. Voir FUTEX_WAIT sur la maniĂšre d’interprĂ©ter cela correctement en pratique.

FUTEX_WAKE_BITSET

Renvoie le nombre de processus en attente qui ont été réveillés.

FUTEX_LOCK_PI

Renvoie 0 si le futex a appliqué le verrou avec succÚs.

FUTEX_LOCK_PI2

Renvoie 0 si le futex a appliqué le verrou avec succÚs.

FUTEX_TRYLOCK_PI

Renvoie 0 si le futex a appliqué le verrou avec succÚs.

FUTEX_UNLOCK_PI

Renvoie 0 si le futex a correctement enlevé le verrou.

FUTEX_CMP_REQUEUE_PI

Renvoie le nombre total d’élĂ©ments en attente rĂ©veillĂ©s ou remis dans la file du futex pour le mot futex sur uaddr2 . Si cette valeur est supĂ©rieure Ă  val , la diffĂ©rence devient le nombre d’élĂ©ments en attente remis dans la file du futex pour le mot futex sur uaddr2 .

FUTEX_WAIT_REQUEUE_PI

Renvoie 0 si l’appelant a Ă©tĂ© mis dans la file d’attente avec succĂšs au futex pour le mot futex sur uaddr2 .

ERREURS

EACCES

Pas d’accĂšs en lecture Ă  la mĂ©moire d’un mot futex.

EAGAIN

( FUTEX_WAIT , FUTEX_WAIT_BITSET , FUTEX_WAIT_REQUEUE_PI ) La valeur vers laquelle pointait uaddr n’était pas Ă©gale Ă  la valeur val attendue au moment de l’appel.

Remarque : sur Linux, les noms symboliques EAGAIN et EWOULDBLOCK (les deux apparaissent dans diffĂ©rents endroits du code futex du noyau) ont la mĂȘme valeur.

EAGAIN

( FUTEX_CMP_REQUEUE , FUTEX_CMP_REQUEUE_PI ) La valeur vers laquelle pointait uaddr n’était pas Ă©gale Ă  la valeur val3 attendue.

EAGAIN

( FUTEX_LOCK_PI , FUTEX_LOCK_PI2 , FUTEX_TRYLOCK_PI , FUTEX_CMP_REQUEUE_PI ) L’ID du thread propriĂ©taire du futex sur uaddr (pour FUTEX_CMP_REQUEUE_PI : uaddr2 ) est sur le point de se terminer, mais il n’a pas encore gĂ©rĂ© le nettoyage de l’état interne. RĂ©essayez.

EDEADLK

( FUTEX_LOCK_PI , FUTEX_LOCK_PI2 , FUTEX_TRYLOCK_PI , FUTEX_CMP_REQUEUE_PI ) Le mot futex sur uaddr est dĂ©jĂ  verrouillĂ© par l’appelant.

EDEADLK

( FUTEX_CMP_REQUEUE_PI ) Pendant qu’il remettait en attente un Ă©lĂ©ment du PI futex pour le mot futex sur uaddr2 , le noyau a dĂ©tectĂ© un verrou mort (deadlock).

EFAULT

Le paramĂštre d’un pointeur nĂ©cessaire (c’est-Ă -dire uaddr , uaddr2 ou timeout ) ne pointait pas vers une adresse valable de l’espace utilisateur.

EINTR

Une opĂ©ration FUTEX_WAIT ou FUTEX_WAIT_BITSET a Ă©tĂ© interrompue par un signal (voir signal (7)). Dans Linux 2.6.22, cette erreur pouvait aussi ĂȘtre renvoyĂ©e pour un faux rĂ©veil ; depuis Linux 2.6.22, cela n’arrive plus.

EINVAL

L’opĂ©ration dans futex_op fait partie de celles qui utilisent un dĂ©lai, mais le paramĂštre timeout fourni n’était pas valable ( tv_sec valait moins de 0 ou tv_nsec ne valait pas moins de 1 000 000 000).

EINVAL

L’opĂ©ration indiquĂ©e dans futex_op utilise uaddr et/ou uaddr2 mais l’un d’eux ne pointe pas vers un objet valable — c’est-Ă -dire, l’adresse n’est pas alignĂ©e sur quatre octets.

EINVAL

( FUTEX_WAIT_BITSET , FUTEX_WAKE_BITSET ) Le masque de bit fourni dans val3 vaut zéro.

EINVAL

( FUTEX_CMP_REQUEUE_PI ) uaddr est Ă©gal Ă  uaddr2 (c’est-Ă -dire qu’une remise en attente a Ă©tĂ© tentĂ©e sur le mĂȘme futex).

EINVAL

( FUTEX_FD ) Le numĂ©ro du signal fourni dans val n’est pas valable.

EINVAL

( FUTEX_WAKE , FUTEX_WAKE_OP , FUTEX_WAKE_BITSET , FUTEX_REQUEUE , FUTEX_CMP_REQUEUE ) Le noyau a dĂ©tectĂ© une incohĂ©rence entre l’état de l’espace utilisateur sur uaddr et l’état du noyau — c’est-Ă -dire qu’il a dĂ©tectĂ© un Ă©lĂ©ment qui attend dans FUTEX_LOCK_PI ou FUTEX_LOCK_PI2 sur uaddr .

EINVAL

( FUTEX_LOCK_PI , FUTEX_LOCK_PI2 , FUTEX_TRYLOCK_PI , FUTEX_UNLOCK_PI ) Le noyau a dĂ©tectĂ© une incohĂ©rence entre l’état de l’espace utilisateur sur uaddr et l’état du noyau. Cela indique soit une corruption d’état, soit que le noyau a trouvĂ© un Ă©lĂ©ment en attente sur uaddr qui attend aussi Ă  l’aide de FUTEX_WAIT ou de FUTEX_WAIT_BITSET .

EINVAL

( FUTEX_CMP_REQUEUE_PI ) Le noyau a dĂ©tectĂ© une incohĂ©rence entre l’état de l’espace utilisateur sur uaddr et l’état du noyau ; c’est-Ă -dire qu’il a dĂ©tectĂ© un Ă©lĂ©ment qui attend via FUTEX_WAIT ou FUTEX_WAIT_BITSET sur uaddr2 .

EINVAL

( FUTEX_CMP_REQUEUE_PI ) Le noyau a dĂ©tectĂ© une incohĂ©rence entre l’état de l’espace utilisateur sur uaddr et l’état du noyau ; c’est-Ă -dire qu’il a dĂ©tectĂ© un Ă©lĂ©ment qui attend Ă  l’aide de FUTEX_WAIT ou de FUTEX_WAIT_BITESET sur uaddr .

EINVAL

( FUTEX_CMP_REQUEUE_PI ) Le noyau a dĂ©tectĂ© une incohĂ©rence entre l’état de l’espace utilisateur sur uaddr et l’état du noyau ; c’est-Ă -dire qu’il a dĂ©tectĂ© un Ă©lĂ©ment qui attend Ă  l’aide de FUTEX_LOCK_PI ou de FUTEX_LOCK_PI2 (au lieu de FUTEX_WAIT_REQUEUE_PI ).

EINVAL

( FUTEX_CMP_REQUEUE_PI ) Tentative de remise dans la file d’un Ă©lĂ©ment en attente vers un futex diffĂ©rent de celui indiquĂ© avec l’appel FUTEX_WAIT_REQUEUE_PI correspondant pour cet Ă©lĂ©ment.

EINVAL

( FUTEX_CMP_REQUEUE_PI ) Le paramĂštre val ne vaut pas 1 .

EINVAL

Argument incorrect.

ENFILE

( FUTEX_FD ) La limite du nombre total de fichiers ouverts sur le systÚme a été atteinte.

ENOMEM

( FUTEX_LOCK_PI , FUTEX_LOCK_PI2 , FUTEX_TRYLOCK_PI , FUTEX_CMP_REQUEUE_PI ) Le noyau n’a pas pu allouer de la mĂ©moire pour conserver les informations d’état.

ENOSYS

Opération non valable indiquée dans futex_op .

ENOSYS

L’option FUTEX_CLOCK_REALTIME Ă©tait indiquĂ©e dans futex_op , mais l’opĂ©ration qui l’accompagne n’est ni FUTEX_WAIT , ni FUTEX_WAIT_BITSET , ni FUTEX_WAIT_REQUEUE_PI , ni FUTEX_LOCK_PI2 .

ENOSYS

( FUTEX_LOCK_PI , FUTEX_LOCK_PI2 , FUTEX_TRYLOCK_PI , FUTEX_UNLOCK_PI , FUTEX_CMP_REQUEUE_PI , FUTEX_WAIT_REQUEUE_PI ) Une vĂ©rification pendant l’exĂ©cution a dĂ©terminĂ© que l’opĂ©ration n’est pas disponible. Les opĂ©rations PI-futex ne sont pas implĂ©mentĂ©es sur toutes les architectures et ne sont pas prises en charge sur certaines variantes de processeur.

EPERM

( FUTEX_LOCK_PI , FUTEX_LOCK_PI2 , FUTEX_TRYLOCK_PI , FUTEX_CMP_REQUEUE_PI ) L’appelant n’est pas autorisĂ© Ă  se rattacher au futex sur uaddr (pour FUTEX_CMP_REQUEUE_PI : le futex sur uaddr2 ) (cela peut venir d’une corruption de l’état dans l’espace utilisateur).

EPERM

( FUTEX_UNLOCK_PI ) Le verrou reprĂ©sentĂ© par le mot futex n’appartient pas Ă  l’appelant.

ESRCH

( FUTEX_LOCK_PI , FUTEX_LOCK_PI2 , FUTEX_TRYLOCK_PI , FUTEX_CMP_REQUEUE_PI ) L’ID du thread dans le mot futex sur uaddr n’existe pas.

ESRCH

( FUTEX_CMP_REQUEUE_PI ) L’ID du thread dans le mot futex sur uaddr2 n’existe pas.

ETIMEDOUT

L’opĂ©ration de futex_op a utilisĂ© un dĂ©lai indiquĂ© dans timeout et le dĂ©lai a expirĂ© avant la fin de l’opĂ©ration.

STANDARDS

Linux.

HISTORIQUE

Linux 2.6.0.

La prise en charge initiale des futex a été ajoutée dans Linux 2.5.7 mais avec une sémantique différente de celle décrite ci-dessus. Un appel systÚme à 4 paramÚtres avec la sémantique décrite dans cette page a été ajouté dans Linux 2.5.40. Dans Linux 2.5.70, un cinquiÚme paramÚtre a été ajouté. Un sixiÚme paramÚtre a été ajouté dans Linux 2.6.7.

EXEMPLES

Le programme ci-dessous montre l’utilisation des futex dans un programme oĂč un processus parent et un processus enfant utilisent une paire de futex situĂ©e dans un tableau anonyme partagĂ© pour synchroniser l’accĂšs Ă  une ressource partagĂ©e : le terminal. Les deux processus Ă©crivent chacun un message nloops (un paramĂštre en ligne de commande qui vaut 5 par dĂ©faut s’il est absent) sur le terminal et ils utilisent un protocole de synchronisation pour garantir qu’ils alternent dans l’écriture des messages. Pendant l’exĂ©cution de ce programme, nous voyons un affichage comme suit :

$ ./futex_demo
Parent (18534) 0
Child (18535) 0
Parent (18534) 1
Child (18535) 1
Parent (18534) 2
Child (18535) 2
Parent (18534) 3
Child (18535) 3
Parent (18534) 4
Child (18535) 4

Source du programme

/* futex_demo.c
Usage: futex_demo [nloops]
(Default: 5)
Montrer l’utilisation des futex dans un programme oĂč le parent et
l’enfant utilisent une paire de futex situĂ©e dans un tableau anonyme
partagĂ© pour synchroniser l’accĂšs Ă  une ressource partagĂ©e : le
terminal. Les processus écrivent chacun des messages 'num-loops'
sur le terminal et ils utilisent un protocole de synchronisation qui
garantit qu’ils alternent l’écriture des messages.
*/
#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <linux/futex.h>
#include <stdatomic.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <unistd.h>
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);
}
/* Acquérir le futex vers lequel pointe 'futexp' : attendre que sa
valeur passe Ă  1 puis positionner la valeur sur 0. */
static void
fwait(uint32_t *futexp)
{
long s;
const uint32_t one = 1;
/* atomic_compare_exchange_strong(ptr, oldval, newval)
fait atomiquement comme :
if (*ptr == *oldval)
*ptr = newval;
Il renvoie true si le test a montré true et *ptr a été mis à jour. */
while (1) {
/* Le futex est-il disponible ? */
if (atomic_compare_exchange_strong(futexp, &one, 0))
break; /* Oui */
/* Le futex n’est pas disponible ; attendre. */
s = futex(futexp, FUTEX_WAIT, 0, NULL, NULL, 0);
if (s == -1 && errno != EAGAIN)
err(EXIT_FAILURE, "futex-FUTEX_WAIT");
}
}
/* Relùcher le futex vers lequel pointe 'futexp' : si le futex a
actuellement la valeur 0, positionner la valeur à 1 et réveiller tous les
futex en attente pour que si le pair est bloqué dans fwait(), ça puisse
continuer. */
static void
fpost(uint32_t *futexp)
{
long s;
const uint32_t zero = 0;
/* atomic_compare_exchange_strong() a été décrit
dans les commentaires ci-dessus. */
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;
/* Créer un tableau anonyme partagé qui gardera les futex.
Comme les futex vont ĂȘtre partagĂ©s entre les processus, nous
utilisons donc les opérations futex « shared » (donc pas celles
dont le suffixe est "_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 */
/* Créer un processus enfant qui hérite du tableau anonyme
partagé. */
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\n", (intmax_t) getpid(), j);
fpost(futex2);
}
exit(EXIT_SUCCESS);
}
/* Le parent se retrouve ici. */
for (unsigned int j = 0; j < nloops; j++) {
fwait(futex2);
printf("Parent (%jd) %u\n", (intmax_t) getpid(), j);
fpost(futex1);
}
wait(NULL);
exit(EXIT_SUCCESS);
}

VOIR AUSSI

get_robust_list (2), restart_syscall (2), pthread_mutexattr_getprotocol (3), futex (7), sched (7)

Les fichiers suivants des sources du noyau :

-

Documentation/pi-futex.txt

-

Documentation/futex-requeue-pi.txt

-

Documentation/locking/rt-mutex.txt

-

Documentation/locking/rt-mutex-design.txt

-

Documentation/robust-futex-ABI.txt

Franke, H., Russell, R., and Kirwood, M., 2002. Fuss, Futexes and Furwocks: Fast Userlevel Locking in Linux (à partir des actions d’Ottawa Linux Symposium 2002),
http://kernel.org/doc/ols/2002/ols2002-pages-479-495.pdf

Hart, D., 2009. A futex overview and update , http://lwn.net/Articles/360699/

Hart, D. et Guniguntala, D., 2009. Requeue-PI: Making Glibc Condvars PI-Aware (à partir des comptes rendus de l’atelier Real-Time Linux 2009), http://lwn.net/images/conf/rtlws11/papers/proc/p10.pdf

Drepper, U., 2011. Futexes Are Tricky , http://www.akkadia.org/drepper/futex.pdf

La bibliothùque d’exemples de futex, futex-*.tar.bz2 à
https://mirrors.kernel.org/pub/linux/kernel/people/rusty/

TRADUCTION

La traduction française de cette page de manuel a été créée par Christophe Blaess <https://www.blaess.fr/christophe/>, Stéphan Rafin <stephan.rafin@laposte.net>, Thierry Vignaud <tvignaud@mandriva.com>, François Micaux, Alain Portal <aportal@univ-montp2.fr>, Jean-Philippe Guérard <fevrier@tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centraliens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <simon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@debian.org>, David Prévot <david@tilapin.org> et Jean-Philippe MENGUAL <jpmengual@debian.org>

Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n’y a aucune RESPONSABILITÉ LÉGALE.

Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à debian-l10n-french@lists.debian.org .