dwww Home | Manual pages | Find package

futex(2)                      System Calls Manual                     futex(2)

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  condi-
       tion  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'ef-
       fectue 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  fu-
       tex » —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 va-
       riable 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 four-
       nie par le thread appelant (en tant qu'un des paramètres de l'appel fu-
       tex())  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'im-
       plémentation  du  blocage  par  le noyau. Tout comme une opération com-
       pare-and-exchange atomique qui modifie potentiellement la mémoire  par-
       tagée,  le  blocage  par futex est une opération compare-and-block ato-
       mique.

       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'ef-
       fectuent  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 va-
       leur 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éini-
       tialiser 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  pen-
       dant  que  les  opérations  telles  que FUTEX_WAIT, décrite ci-dessous,
       s'effectuent sur un mot futex en particulier.

   Arguments
       Le paramètre uaddr pointe vers un mot  futex.  Sur  toutes  les  plate-
       formes,  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 argu-
       ments n'est pas nécessaire, il est ignoré.

       Pour plusieurs opérations de blocage, le paramètre timeout est un poin-
       teur  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 in-
       terpré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 fu-
              tex. Il dit au noyau que le futex est  un  processus  privé  non
              partagé  avec  d'autres processus (c'est-à-dire qu'il n'est uti-
              lisé que pour la synchronisation entre les threads du même  pro-
              cessus).  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  FU-
              TEX_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  FU-
              TEX_WAIT_BITSET,  FUTEX_WAIT_REQUEUE_PI  (depuis Linux 4.5), FU-
              TEX_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  ti-
              meout 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 FU-
              TEX_WAKE sur le mot futex. Le chargement de la valeur du mot fu-
              tex 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 fu-
              tex. 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 choi-
              sie en indiquant FUTEX_CLOCK_REALTIME dans futex_op. Si  le  ti-
              meout 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  FU-
              TEX_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érale-
              ment,  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 ren-
              voyé 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  se-
              lect(2), poll(2), et epoll(7)

              Le descripteur de fichier peut être utilisé pour avoir des noti-
              fications asynchrones, si val n'est  pas  nul,  puis,  quand  un
              autre  processus  ou un autre thread exécute FUTEX_WAKE, l'appe-
              lant 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 sui-
              vants.

       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  sup-
              primé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 at-
              tente 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  FU-
              TEX_CMP_REQUEUE  équivalente à FUTEX_WAKE). La valeur limite in-
              diqué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 FU-
              TEX_CMP_REQUEUE équivalente à FUTEX_WAIT).

              L'opération FUTEX_CMP_REQUEUE a été ajoutée pour remplacer l'an-
              cienne  FUTEX_REQUEUE.  La différence est que la vérification de
              la valeur sur uaddr peut être utilisée pour s'assurer que la re-
              mise  en attente ne se produit que sous certaines conditions, ce
              qui évite les conflits de mémoire (race  conditions)  dans  cer-
              tains 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 fu-
              tex.  Imaginons  le  scénario  suivant  où plusieurs threads at-
              tendent en B, une file d'attente implémentée en utilisant un fu-
              tex :

                  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  bloque-
              raient  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éra-
              tions sur deux futex, une pour  implémenter  le  mutex,  l'autre
              pour  utiliser  dans l'implémentation de la file d'attente asso-
              cié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éra-
              tions 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 ef-
                 fectue  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 ma-
                 chine 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'enco-
              dage 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  nu-
              mé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 des-
              cription 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 pa-
              ramètre val3 est utilisé pour fournir un masque  de  bit  de  32
              bits  au  noyau.  Ce  masque, où au moins un bit doit être posi-
              tionné, 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  utili-
              sant  FUTEX_WAIT_BITSET). Tous les éléments en attente pour les-
              quels le AND est positif sont réveillés ; les autres restent en-
              dormis.

              L'effet de FUTEX_WAIT_BITSET et de FUTEX_WAKE_BITSET est de per-
              mettre 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'uti-
              lisation de cette fonction de mélange de masques de bit  sur  un
              futex peut être moins efficace que le fait d'avoir plusieurs fu-
              tex, 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_BITSETval3 est indi-
              qué 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_BITSETval3 est indi-
              qué 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 priori-
       té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'inver-
       sion 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 re-
       lâ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  ni-
       veau 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'utilisa-
       tion 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éra-
       tions  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 concur-
          rence 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   com-
       pare-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 FU-
       TEX_WAITERS  est défini dans la valeur du futex ; dans ce cas le déten-
       teur 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  utili-
       sateur.

       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  va-
       leur  0,  ou en ayant des éléments en attente mais aucun bit FUTEX_WAI-
       TERS 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-mu-
       tex 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-des-
       sous dans futex_op. Remarquez que les opérations de  PI  futex  doivent
       être  utilisées  par paires et sont soumises à des exigences supplémen-
       taires :

       •  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 fu-
          tex 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  ob-
          tient 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 va-
              leur 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 fu-
                   tex 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  ver-
              rouillage. 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 in-
              dé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'hor-
              loge  par rapport à laquelle timeout est mesuré peut être sélec-
              tionnée. Par défaut, le délai (absolu) indiqué dans timeout  est
              mesuré  par rapport à l'horloge CLOCK_MONOTONIC mais si l'attri-
              but 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 ap-
              pelé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 FU-
              TEX_OWNER_DIED). Cela peut arriver quand le propriétaire du  fu-
              tex  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_RE-
              QUEUE_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  maxi-
              mum 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 FU-
              TEX_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_RE-
              QUEUE_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 synchroni-
              sés. Ainsi, dans l'opération  FUTEX_WAIT_REQUEUE_PI,  l'applica-
              tion dans l'espace utilisateur pré-indique la cible de la remise
              en attente  qui  va  se  faire  dans  l'opération  FUTEX_CMP_RE-
              QUEUE_PI.

VALEUR RENVOYÉE
       En cas d'erreur (en supposant que futex() a été appelé à l’aide de sys-
       call(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'em-
              placement  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  utili-
              sateur) 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 va-
              leur  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 va-
              leur 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 la-
              quelle pointait uaddr n'était pas égale à la valeur val3  atten-
              due.

       EAGAIN (FUTEX_LOCK_PI,  FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI, FUTEX_CMP_RE-
              QUEUE_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_RE-
              QUEUE_PI) Le mot futex sur uaddr est déjà verrouillé par l'appe-
              lant.

       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 Li-
              nux 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 va-
              lable.

       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 FU-
              TEX_LOCK_PI ou FUTEX_LOCK_PI2 sur uaddr.

       EINVAL (FUTEX_LOCK_PI,  FUTEX_LOCK_PI2,   FUTEX_TRYLOCK_PI,   FUTEX_UN-
              LOCK_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  FU-
              TEX_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 FU-
              TEX_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_RE-
              QUEUE_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_RE-
              QUEUE_PI) Le noyau n'a pas pu allouer de la mémoire pour conser-
              ver 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  FU-
              TEX_WAIT_BITSET, ni FUTEX_WAIT_REQUEUE_PI, ni FUTEX_LOCK_PI2.

       ENOSYS (FUTEX_LOCK_PI,  FUTEX_LOCK_PI2,   FUTEX_TRYLOCK_PI,   FUTEX_UN-
              LOCK_PI,  FUTEX_CMP_REQUEUE_PI, FUTEX_WAIT_REQUEUE_PI) Une véri-
              fication 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_RE-
              QUEUE_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 utili-
              sateur).

       EPERM  (FUTEX_UNLOCK_PI) Le verrou représenté par le mot futex n'appar-
              tient pas à l'appelant.

       ESRCH  (FUTEX_LOCK_PI, FUTEX_LOCK_PI2, FUTEX_TRYLOCK_PI,  FUTEX_CMP_RE-
              QUEUE_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.

VERSIONS
       Les futex ont d'abord été disponibles dans une version stable du  noyau
       avec 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  ap-
       pel 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.

STANDARDS
       Cet appel système est spécifique à Linux.

NOTES
       Plusieurs abstractions programmatiques de haut niveau sont implémentées
       avec des futex, notamment les mécanismes POSIX de sémaphore et de  syn-
       chronisation  de  threads (mutex, variables conditionnelles, verrous en
       lecture/écriture et barrières).

EXEMPLES
       Le programme ci-dessous montre l'utilisation des  futex  dans  un  pro-
       gramme  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

          Utilisation: futex_demo [nloops]
                           (Par défaut : 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;        /* État : indisponible */
           *futex2 = 1;        /* État : disponible */

           /* 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("Enfant (%jd) %d\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) %d\n", (intmax_t) getpid(), j);
               fpost(futex1);
           }

           wait(NULL);

           exit(EXIT_SUCCESS);
       }

VOIR AUSSI
       get_robust_list(2), restart_syscall(2), pthread_mutexattr_getproto-
       col(3), futex(7), sched(7)

       Les fichiers suivants des sources du noyau :

       •  Documentation/pi-futex.txtDocumentation/futex-requeue-pi.txtDocumentation/locking/rt-mutex.txtDocumentation/locking/rt-mutex-design.txtDocumentation/robust-futex-ABI.txt

       Franke, H., Russell, R., and Kirwood, M., 2002. Fuss, Futexes and Fur-
       wocks: 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 Chris-
       tophe Blaess <https://www.blaess.fr/christophe/>, Stéphan Rafin <ste-
       phan.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.cou-
       lon@wanadoo.fr>, Julien Cristau <jcristau@debian.org>, Thomas Huriaux
       <thomas.huriaux@gmail.com>, Nicolas François <nicolas.francois@centra-
       liens.net>, Florentin Duneau <fduneau@gmail.com>, Simon Paillard <si-
       mon.paillard@resel.enst-bretagne.fr>, Denis Barbier <barbier@de-
       bian.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
       ⟨https://www.gnu.org/licenses/gpl-3.0.html⟩ 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⟩.

Pages du manuel de Linux 6.03   5 février 2023                        futex(2)

Generated by dwww version 1.15 on Sat Jun 29 01:47:50 CEST 2024.