.\" -*- coding: UTF-8 -*- .\" Page by b.hubert .\" and Copyright (C) 2015, Thomas Gleixner .\" and Copyright (C) 2015, Michael Kerrisk .\" .\" %%%LICENSE_START(FREELY_REDISTRIBUTABLE) .\" may be freely modified and distributed .\" %%%LICENSE_END .\" .\" Niki A. Rahimi (LTC Security Development, narahimi@us.ibm.com) .\" added ERRORS section. .\" .\" Modified 2004-06-17 mtk .\" Modified 2004-10-07 aeb, added FUTEX_REQUEUE, FUTEX_CMP_REQUEUE .\" .\" FIXME Still to integrate are some points from Torvald Riegel's mail of .\" 2015-01-23: .\" http://thread.gmane.org/gmane.linux.kernel/1703405/focus=7977 .\" .\" FIXME Do we need to add some text regarding Torvald Riegel's 2015-01-24 mail .\" http://thread.gmane.org/gmane.linux.kernel/1703405/focus=1873242 .\" .\"******************************************************************* .\" .\" This file was generated with po4a. Translate the source file. .\" .\"******************************************************************* .TH futex 2 "3 mai 2023" "Pages du manuel de Linux 6.05.01" .SH NOM futex – Verrouillage rapide en mode utilisateur .SH BIBLIOTHÈQUE Bibliothèque C standard (\fIlibc\fP, \fI\-lc\fP) .SH SYNOPSIS .nf .PP \fB#include \fP /* Définition des constantes \fBFUTEX_*\fP */ \fB#include \fP /* Définition des constantes \fBSYS_*\fP */ \fB#include \fP .PP \fBlong syscall(SYS_futex, uint32_t *\fP\fIuaddr\fP\fB, int \fP\fIfutex_op\fP\fB, uint32_t \fP\fIval\fP\fB,\fP \fB const struct timespec *\fP\fItimeout\fP\fB,\fP\fI \fP /* ou\ : \fBuint32_t \fP\fIval2\fP\fB */\fP \fB uint32_t *\fP\fIuaddr2\fP\fB, uint32_t \fP\fIval3\fP\fB);\fP .fi .PP \fIRemarque\fP\ : la glibc ne fournit pas de fonction autour de \fBfutex\fP(), nécessitant l'utilisation de \fBsyscall\fP(2). .SH DESCRIPTION L'appel système \fBfutex\fP() 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 \fBfutex\fP() que lorsqu'il est probable qu'il doive se bloquer plus longtemps avant que la condition ne soit vraie. D'autres opérations \fBfutex\fP() peuvent être utilisées pour réveiller des processus ou des threads qui attendent une condition en particulier. .PP Un futex est une valeur 32 bits —\ désignée ci\-dessous comme «\ \fImot futex\fP\ »\ —dont l'adresse est fournie à l'appel système \fBfutex\fP() (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) \fBmmap\fP(2) ou \fBshmat\fP(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. .PP .\" Notes from Darren Hart (Dec 2015): .\" Totally ordered with respect futex operations refers to semantics .\" of the ACQUIRE/RELEASE operations and how they impact ordering of .\" memory reads and writes. The kernel futex operations are protected .\" by spinlocks, which ensure that all operations are serialized .\" with respect to one another. .\" .\" This is a lot to attempt to define in this document. Perhaps a .\" reference to linux/Documentation/memory-barriers.txt as a footnote .\" would be sufficient? Or perhaps for this manual, "serialized" would .\" be sufficient, with a footnote regarding "totally ordered" and a .\" pointer to the memory-barrier documentation? .\" FIXME(Torvald Riegel): .\" Eventually we want to have some text in NOTES to satisfy .\" the reference in the following sentence .\" See NOTES for a detailed specification of .\" the synchronization semantics. 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 \fBfutex\fP()) 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. .PP 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 \fBfutex\fP(). Cette opération \fBfutex\fP() 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 \fBfutex\fP(7) pour plus de détails sur la manière d'utiliser les futex. .PP 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. .PP .\" 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 \fBFUTEX_WAIT\fP, décrite ci\-dessous, s'effectuent sur un mot futex en particulier. .SS Arguments Le paramètre \fIuaddr\fP 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 \fIfutex_op\fP\ ; \fIval\fP est une valeur dont la signification et l'objectif dépendent de \fIfutex_op\fP. .PP Les autres paramètres (\fItimeout\fP, \fIuaddr2\fP et \fIval3\fP) 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é. .PP Pour plusieurs opérations de blocage, le paramètre \fItimeout\fP est un pointeur vers une structure \fItimespec\fP 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 \fItimeout\fP d'abord à \fIunsigned long\fP, puis à \fIuint32_t\fP, et dans le reste de cette page, ce paramètre est désigné par \fIval2\fP quand il est interprété de cette manière. .PP Lorsqu'il est nécessaire, le paramètre \fIuaddr2\fP est un pointeur vers un deuxième mot futex utilisé par l'opération. .PP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" L'interprétation du paramètre de l'entier final, \fIval3\fP, dépend de l'opération. .SS "Opérations futex" Le paramètre \fIfutex_op\fP 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 \fIfutex_op\fP sont les suivantes\ : .TP \fBFUTEX_PRIVATE_FLAG\fP (depuis Linux 2.6.22) .\" commit 34f01cc1f512fa783302982776895c73714ebbc2 .\" I.e., It allows the kernel choose the fast path for validating .\" the user-space address and avoids expensive VMA lookups, .\" taking reference counts on file backing store, and so on. 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. .IP .\" except the obsolete FUTEX_FD, for which the "private" flag was .\" meaningless Par commodité, \fI\fP définit un ensemble de constantes dont le suffixe est \fB_PRIVATE\fP et qui sont équivalentes à toutes les opérations listées ci\-dessous mais avec l'attribut \fBFUTEX_PRIVATE_FLAG\fP ORed dans la valeur de la constante. On trouve ainsi \fBFUTEX_WAIT_PRIVATE\fP, \fBFUTEX_WAKE_PRIVATE\fP et ainsi de suite. .TP \fBFUTEX_CLOCK_REALTIME\fP (depuis Linux 2.6.28) .\" commit 1acdac104668a0834cfa267de9946fac7764d486 .\" commit 337f13046ff03717a9e99675284a817527440a49 .\" commit bf22a6976897977b0a3f1aeba6823c959fc4fdae Ce bit d'option ne peut être utilisé qu'avec les opérations \fBFUTEX_WAIT_BITSET\fP, \fBFUTEX_WAIT_REQUEUE_PI\fP (depuis Linux 4.5), \fBFUTEX_WAIT\fP (depuis Linux 4.5) et \fBFUTEX_LOCK_PI2\fP (depuis Linux 5.14). .IP Si cette option est positionnée, le noyau mesure le \fItimeout\fP par rapport à l'horloge \fBCLOCK_REALTIME\fP. .IP Si cette option n'est pas positionnée, le noyau mesure le \fItimeout\fP par rapport à l'horloge \fBCLOCK_MONOTONIC\fP. .PP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" L'opération indiquée dans \fIfutex_op\fP prend une de ces valeurs\ : .TP \fBFUTEX_WAIT\fP (depuis Linux 2.6.0) .\" Strictly speaking, since some time in Linux 2.5.x .\" FIXME: Torvald, I think we may need to add some explanation of .\" "totally ordered" here. Cette option teste que la valeur du mot futex vers laquelle pointe l'adresse \fIuaddr\fP contient toujours la valeur \fIval\fP attendue, et si tel est le cas, elle s'endort jusqu'à une opération \fBFUTEX_WAKE\fP 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 à \fIval\fP, l'appel échoue immédiatement avec l'erreur \fBEAGAIN\fP. .IP 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 \fBFUTEX_WAKE\fP (ou un réveil équivalent) après le changement de cette valeur et avant cette opération \fBFUTEX_WAIT\fP, le thread appelant observera cette valeur et ne commencera pas à dormir. .IP Si le \fItimeout\fP 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 \fBCLOCK_MONOTONIC\fP mais depuis Linux 4.5, l'horloge \fBCLOCK_REALTIME\fP peut être choisie en indiquant \fBFUTEX_CLOCK_REALTIME\fP dans \fIfutex_op\fP. Si le \fItimeout\fP est NULL, l'appel se bloque indéfiniment. .IP \fIRemarque\fP\ : pour \fBFUTEX_WAIT\fP, le \fItimeout\fP est interprété comme une \fIvaleur\fP relative. Cela diffère des autres opérations futex où le \fItimeout\fP est interprété comme une valeur absolue. Pour obtenir l'équivalent de \fBFUTEX_WAIT\fP, avec un délai absolu, utilisez \fBFUTEX_WAIT_BITSET\fP en indiquant \fIval3\fP comme \fBFUTEX_BITSET_MATCH_ANY\fP. .IP .\" FIXME . (Torvald) I think we should remove this. Or maybe adapt to a .\" different example. .\" .\" For .\" .BR futex (7), .\" this call is executed if decrementing the count gave a negative value .\" (indicating contention), .\" and will sleep until another process or thread releases .\" the futex and executes the .\" .B FUTEX_WAKE .\" operation. .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Les paramètres \fIuaddr2\fP et \fIval3\fP sont ignorés. .TP \fBFUTEX_WAKE\fP (depuis Linux 2.6.0) .\" Strictly speaking, since Linux 2.5.x Cette opération réveille jusqu'à \fIval\fP éléments en attente (comme dans \fBFUTEX_WAIT\fP) sur le mot futex à l'adresse \fIuaddr\fP. Généralement, \fIval\fP est indiqué soit sous la forme de \fB1\fP (réveil d'un seul élément en attente) soit avec \fBINT_MAX\fP (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). .IP .\" FIXME . (Torvald) I think we should remove this. Or maybe adapt to .\" a different example. .\" .\" For .\" .BR futex (7), .\" this is executed if incrementing the count showed that .\" there were waiters, .\" once the futex value has been set to 1 .\" (indicating that it is available). .\" .\" How does "incrementing the count show that there were waiters"? .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Les paramètres \fItimeout\fP, \fIuaddr2\fP et \fIval3\fP sont ignorés. .TP \fBFUTEX_FD\fP (de Linux 2.6.0 jusqu'à Linux 2.6.25 inclus) .\" Strictly speaking, from Linux 2.5.x to Linux 2.6.25 Cette opération crée un descripteur de fichier associé au futex sur \fIuaddr\fP. L'appelant doit fermer le descripteur de fichier renvoyé après l'avoir utilisé. Quand un autre processus ou un autre thread effectue un \fBFUTEX_WAKE\fP sur le mot futex, le descripteur de fichier indique qu'il est accessible en lecture avec \fBselect\fP(2), \fBpoll\fP(2), et \fBepoll\fP(7) .IP Le descripteur de fichier peut être utilisé pour avoir des notifications asynchrones, si \fIval\fP n'est pas nul, puis, quand un autre processus ou un autre thread exécute \fBFUTEX_WAKE\fP, l'appelant recevra le numéro du signal passé à \fIval\fP. .IP Les paramètres \fItimeout\fP, \fIuaddr2\fP et \fIval3\fP sont ignorés. .IP .\" commit 82af7aca56c67061420d618cc5a30f0fd4106b80 .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Parce qu'il était de façon inhérente sujet à des situations de concurrence, \fBFUTEX_FD\fP a été supprimé de Linux\ 2.6.26 et les suivants. .TP \fBFUTEX_REQUEUE\fP (depuis Linux 2.6.0) .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Cette opération effectue la même chose que \fBFUTEX_CMP_REQUEUE\fP (voir ci\-dessous), sauf qu'elle ne vérifie rien en utilisant la valeur dans \fIval3\fP (le paramètre \fIval3\fP est ignoré). .TP \fBFUTEX_CMP_REQUEUE\fP (depuis Linux 2.6.7) Cette opération vérifie d'abord si l'emplacement \fIuaddr\fP contient toujours la valeur \fIval3\fP. Si tel n'est pas le cas, l'opération échoue avec l'erreur \fBEAGAIN\fP. Si tel est le cas, l'opération réveille un maximum de \fIval\fP éléments en attente du futex sur \fIuaddr\fP. S'il y a plus de \fIval\fP éléments en attente, les autres sont supprimés de la file d'attente du futex source sur \fIuaddr\fP et ajoutés à la file d'attente du futex cible sur \fIuaddr2\fP. Le paramètre \fIval2\fP indique une limite supérieure du nombre d'éléments remis en attente dans le futex sur \fIuaddr2\fP. .IP .\" FIXME(Torvald) Is the following correct? Or is just the decision .\" which threads to wake or requeue part of the atomic operation? .\" Notes from a f2f conversation with Thomas Gleixner (Aug 2015): ### .\" The operation is serialized with respect to operations on both .\" source and target futex. No other waiter can enqueue itself .\" for waiting and no other waiter can dequeue itself because of .\" a timeout or signal. Le chargement à partir de \fIuaddr\fP 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 \fIval3\fP 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. .IP Les valeurs classiques qu'on indique à \fIval\fP sont \fB0\fP ou \fB1\fP (indiquer \fBINT_MAX\fP n'est pas utile car cela rendrait l'opération \fBFUTEX_CMP_REQUEUE\fP équivalente à \fBFUTEX_WAKE\fP). La valeur limite indiquée avec \fIval2\fP est généralement \fB1\fP ou \fBINT_MAX\fP (indiquer \fB0\fP en paramètre n'est pas utile car cela rendrait l'opération \fBFUTEX_CMP_REQUEUE\fP équivalente à \fBFUTEX_WAIT\fP). .IP .\" But, as Rich Felker points out, there remain valid use cases for .\" FUTEX_REQUEUE, for example, when the calling thread is requeuing .\" the target(s) to a lock that the calling thread owns .\" From: Rich Felker .\" Date: Wed, 29 Oct 2014 22:43:17 -0400 .\" To: Darren Hart .\" CC: libc-alpha@sourceware.org, ... .\" Subject: Re: Add futex wrapper to glibc? L'opération \fBFUTEX_CMP_REQUEUE\fP a été ajoutée pour remplacer l'ancienne \fBFUTEX_REQUEUE\fP. La différence est que la vérification de la valeur sur \fIuaddr\fP 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. .IP \fBFUTEX_REQUEUE\fP et \fBFUTEX_CMP_REQUEUE\fP peuvent être utilisées pour éviter des réveils en troupeau (thundering herd) qui peuvent survenir quand on utilise \fBFUTEX_WAKE\fP 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\ : .IP .in +4n .EX lock(A) while (!check_value(V)) { unlock(A); block_on(B); lock(A); }; unlock(A); .EE .in .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Si un thread qui se réveille utilisait \fBFUTEX_WAKE\fP, 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. .TP \fBFUTEX_WAKE_OP\fP (depuis Linux 2.6.14) .\" commit 4732efbeb997189d9f9b04708dc26bf8613ed721 .\" Author: Jakub Jelinek .\" Date: Tue Sep 6 15:16:25 2005 -0700 .\" FIXME. (Torvald) The glibc condvar implementation is currently being .\" revised (e.g., to not use an internal lock anymore). .\" It is probably more future-proof to remove this paragraph. .\" [Torvald, do you have an update here?] 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 \fBpthread_cond_signal\fP(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. \fBFUTEX_WAKE_OP\fP permet d'implémenter de tels cas sans augmenter le nombre de conflits et de changement de contexte. .IP L'opération \fBFUTEX_ WAKE_OP\fP 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\ : .IP .in +4n .EX uint32_t oldval = *(uint32_t *) uaddr2; *(uint32_t *) uaddr2 = oldval \fIop\fP \fIoparg\fP; futex(uaddr, FUTEX_WAKE, val, 0, 0, 0); if (oldval \fIcmp\fP \fIcmparg\fP) futex(uaddr2, FUTEX_WAKE, val2, 0, 0, 0); .EE .in .IP En d'autres termes, \fBFUTEX_WAKE_OP\fP fait ce qui suit\ : .RS .IP \- 3 sauvegarde la valeur d'origine du mot futex sur \fIuaddr2\fP et effectue une opération pour modifier la valeur du futex sur \fIuaddr2\fP\ ; 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) .IP \- réveille un maximum de \fIval\fP éléments en attente sur le futex pour le mot futex sur \fIuaddr\fP\ ; .IP \- et selon les résultats d'un test de la valeur d'origine du mot futex sur \fIuaddr2\fP, réveille un maximum de \fIval2\fP éléments en attente du mot futex sur le futex sur \fIuaddr2\fP. .RE .IP L'opération et la comparaison qui doivent être effectuées sont encodées dans les bits du paramètre \fIval3\fP. Visuellement, l'encodage est\ : .IP .in +4n .EX +\-\-\-+\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+ |op |cmp| oparg | cmparg | +\-\-\-+\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+\-\-\-\-\-\-\-\-\-\-\-+ 4 4 12 12 <== # of bits .EE .in .IP Exprimé en code, l'encodage est\ : .IP .in +4n .EX #define FUTEX_OP(op, oparg, cmp, cmparg) \e (((op & 0xf) << 28) | \e ((cmp & 0xf) << 24) | \e ((oparg & 0xfff) << 12) | \e (cmparg & 0xfff)) .EE .in .IP Dans ce qui précède, \fIop\fP et \fIcmp\fP sont chacun des codes listés ci\-dessous. Les composants \fIoparg\fP et \fIcmparg\fP sont des valeurs numériques littérales, sauf les remarques ci\-dessous. .IP Le composant \fIop\fP prend une de ces valeurs\ : .IP .in +4n .EX FUTEX_OP_SET 0 /* uaddr2 = oparg; */ FUTEX_OP_ADD 1 /* uaddr2 += oparg; */ FUTEX_OP_OR 2 /* uaddr2 |= oparg; */ FUTEX_OP_ANDN 3 /* uaddr2 &= \[ti]oparg; */ FUTEX_OP_XOR 4 /* uaddr2 \[ha]= oparg; */ .EE .in .IP En outre, comparer bit à bit (ORing) la valeur suivante dans \fIop\fP a pour conséquence que \fI(1\~<<\~oparg)\fP sera utilisé en tant qu'opérande\ : .IP .in +4n .EX FUTEX_OP_ARG_SHIFT 8 /* Utiliser (1 << oparg) comme opérande */ .EE .in .IP Le champ \fIcmp\fP prend une de ces valeurs\ : .IP .in +4n .EX 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 */ .EE .in .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Le code de retour de \fBFUTEX_WAKE_OP\fP est la somme du nombre d'éléments en attente réveillés par le futex \fIuaddr\fP et du nombre d'éléments en attente réveillés sur le futex \fIuaddr2\fP. .TP \fBFUTEX_WAIT_BITSET\fP (depuis Linux 2.6.25) .\" commit cd689985cf49f6ff5c8eddc48d98b9d581d9475d Cette opération est équivalente à \fBFUTEX_WAIT\fP, sauf que \fIval3\fP 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 \fBFUTEX_WAKE_BITSET\fP pour plus de détails. .IP Si \fItimeout\fP n'est pas NULL, la structure vers laquelle il pointe indique un délai absolu de l'opération d'attente. Si \fItimeout\fP est NULL, l'opération peut se bloquer indéfiniment. .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" L'argument \fIuaddr2\fP est ignoré. .TP \fBFUTEX_WAKE_BITSET\fP (depuis Linux 2.6.25) .\" commit cd689985cf49f6ff5c8eddc48d98b9d581d9475d Cette opération est identique à \fBFUTEX_WAKE\fP, sauf que le paramètre \fIval3\fP 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 \fIval3\fP) 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 \fBFUTEX_WAIT_BITSET\fP). Tous les éléments en attente pour lesquels le AND est positif sont réveillés\ ; les autres restent endormis. .IP .\" According to http://locklessinc.com/articles/futex_cheat_sheet/: .\" .\" "The original reason for the addition of these extensions .\" was to improve the performance of pthread read-write locks .\" in glibc. However, the pthreads library no longer uses the .\" same locking algorithm, and these extensions are not used .\" without the bitset parameter being all ones. .\" .\" The page goes on to note that the FUTEX_WAIT_BITSET operation .\" is nevertheless used (with a bit mask of all ones) in order to .\" obtain the absolute timeout functionality that is useful .\" for efficiently implementing Pthreads APIs (which use absolute .\" timeouts); FUTEX_WAIT provides only relative timeouts. L'effet de \fBFUTEX_WAIT_BITSET\fP et de \fBFUTEX_WAKE_BITSET\fP 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\ »). .IP La constante \fBFUTEX_BITSET_MATCH_ANY\fP, qui correspond à tous les positionnements 32\ bits du masque, peut être utilisé en tant que \fIval3\fP de \fBFUTEX_WAIT_BITSET\fP et de \fBFUTEX_WAKE_BITSET\fP. En dehors des différences dans la gestion du paramètre \fItimeout\fP, l'opération \fBFUTEX_WAIT\fP est équivalente à \fBFUTEX_WAIT_BITSET\fP où \fIval3\fP est indiqué en tant que \fBFUTEX_BITSET_MATCH_ANY\fP\ ; c'est\-à\-dire permettre le réveil par n'importe quel élément en attente). L’opération \fBFUTEX_WAKE\fP est équivalente à \fBFUTEX_WAKE_BITSET\fP où \fIval3\fP est indiqué en tant que \fBFUTEX_BITSET_MATCH_ANY\fP\ ; c'est\-à\-dire, réveiller n’importe quel élément en attente. .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Les arguments \fIuaddr2\fP et \fItimeout\fP sont ignorés. .SS "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. .PP 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é. .PP .\" .\" Quoting Darren Hart: .\" These opcodes paired with the PI futex value policy (described below) .\" defines a "futex" as PI aware. These were created very specifically .\" in support of PI pthread_mutexes, so it makes a lot more sense to .\" talk about a PI aware pthread_mutex, than a PI aware futex, since .\" there is a lot of policy and scaffolding that has to be built up .\" around it to use it properly (this is what a PI pthread_mutex is). 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). .PP .\" mtk: The following text is drawn from the Hart/Guniguntala paper .\" (listed in SEE ALSO), but I have reworded some pieces .\" significantly. .\" 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\ : .IP \- 3 Si le verrou n'est pas acquis, la valeur du mot futex doit être \fB0\fP. .IP \- Si le verrou est acquis, la valeur du mot futex doit être l'ID du thread (TID\ ; voir \fBgettid\fP(2)) du thread propriétaire. .IP \- Si le verrou a un propriétaire et s'il y a des threads en concurrence pour le verrou, le bit \fBFUTEX_WAITERS\fP doit être positionné dans la valeur du mot futex\ ; autrement dit, cette valeur est\ : .IP .in +4n .EX FUTEX_WAITERS | TID .EE .in .IP (Remarquez que cela n'est pas possible pour un mot futex PI d'être sans propriétaire ni \fBFUTEX_WAITERS\fP défini). .PP 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 \fIcmpxchg\fP 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 \fB0\fP. Relâcher un verrou exige d'utiliser compare\-and\-swap pour positionner la valeur du mot futex sur \fB0\fP si la valeur précédente était le TID prévu. .PP 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 \fBFUTEX_LOCK_PI\fP pour acquérir le verrou. Si d'autres threads attendent le verrou, le bit \fBFUTEX_WAITERS\fP est défini dans la valeur du futex\ ; dans ce cas le détenteur du verrou doit utiliser l'opération \fBFUTEX_UNLOCK_PI\fP pour relâcher le verrou. .PP Dans le cas où les appelants sont bloqués dans le noyau (c'est\-à\-dire qu'ils doivent effectuer un appel \fBfutex\fP()), 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. .PP .\" tglx (July 2015): .\" If there are multiple waiters on a pi futex then a wake pi operation .\" will wake the first waiter and hand over the lock to this waiter. This .\" includes handing over the rtmutex which represents the futex in the .\" kernel. The strict requirement is that the futex owner and the rtmutex .\" owner must be the same, except for the update period which is .\" serialized by the futex internal locking. That means the kernel must .\" update the user-space value prior to returning to user space 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 \fB0\fP, ou en ayant des éléments en attente mais aucun bit \fBFUTEX_WAITERS\fP positionné). .PP .\" tglx (July 2015): .\" The FUTEX_OWNER_DIED bit can also be set on uncontended futexes, where .\" the kernel has no state associated. This happens via the robust futex .\" mechanism. In that case the futex value will be set to .\" FUTEX_OWNER_DIED. The robust futex mechanism is also available for non .\" PI futexes. 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 \fBFUTEX_OWNER_DIED\fP 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 \fBFUTEX_OWNER_DIED\fP et il est alors responsable pour nettoyer l'espace laissé par le propriétaire mort. .PP Les PI futex sont utilisés en indiquant une des valeurs listées ci\-dessous dans \fIfutex_op\fP. Remarquez que les opérations de PI futex doivent être utilisées par paires et sont soumises à des exigences supplémentaires\ : .IP \- 3 \fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP et \fBFUTEX_TRYLOCK_PI\fP vont de pair avec \fBFUTEX_UNLOCK_PI\fP. \fBFUTEX_UNLOCK_PI\fP 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 \fBEPERM\fP. .IP \- \fBFUTEX_WAIT_REQUEUE_PI\fP va de pair avec \fBFUTEX_CMP_REQUEUE_PI\fP. Elles doivent s'effectuer depuis un futex non\-PI vers un PI futex distinct (sans quoi on obtient l'erreur \fBEINVAL\fP). De plus, \fIval\fP (le nombre d'éléments en attente à réveiller) doit être de \fB1\fP (sans quoi on obtient l'erreur \fBEINVAL\fP). .PP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Les opérations PI futex sont comme suit\ : .TP \fBFUTEX_LOCK_PI\fP (depuis Linux 2.6.18) .\" commit c87e2837be82df479a6bae9f155c43516d2feebc 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. .IP .\" tglx (July 2015): .\" The operation here is similar to the FUTEX_WAIT logic. When the user .\" space atomic acquire does not succeed because the futex value was non .\" zero, then the waiter goes into the kernel, takes the kernel internal .\" lock and retries the acquisition under the lock. If the acquisition .\" does not succeed either, then it sets the FUTEX_WAITERS bit, to signal .\" the lock owner that it needs to go into the kernel. Here is the pseudo .\" code: .\" .\" lock(kernel_lock); .\" retry: .\" .\" /* .\" * Owner might have unlocked in user space before we .\" * were able to set the waiter bit. .\" */ .\" if (atomic_acquire(futex) == SUCCESS) { .\" unlock(kernel_lock()); .\" return 0; .\" } .\" .\" /* .\" * Owner might have unlocked after the above atomic_acquire() .\" * attempt. .\" */ .\" if (atomic_set_waiters_bit(futex) != SUCCESS) .\" goto retry; .\" .\" queue_waiter(); .\" unlock(kernel_lock); .\" block(); .\" L'opération vérifie la valeur du mot futex sur l'adresse \fIuaddr\fP. Si la valeur est de \fB0\fP, 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 \fBFUTEX_WAITERS\fP, 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 à \fB0\fP. Après cela, le noyau\ : .RS .IP (1) 5 Essaie de trouver le thread associé au TID du propriétaire. .IP (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é. .IP (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). .RE .IP .\" August 2015: .\" mtk: If the realm is restricted purely to SCHED_OTHER (SCHED_NORMAL) .\" processes, does the nice value come into play also? .\" .\" tglx: No. SCHED_OTHER/NORMAL tasks are handled in FIFO order .\" (i.e., task 1 blocks on lock A, held by task 2, .\" while task 2 blocks on lock B, held by task 3) 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 \fBSCHED_DEADLINE\fP, \fBSCHED_FIFO\fP et \fBSCHED_RR\fP dans \fBsched\fP(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 \fBSCHED_DEADLINE\fP ou \fBSCHED_FIFO\fP), soit de la priorité de l'élément en attente (s'il est programmé sous la règle \fBSCHED_RR\fP ou \fBSCHED_FIFO\fP). 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). .IP .\" 2016-07-07 response from Thomas Gleixner on LKML: .\" From: Thomas Gleixner .\" Date: 6 July 2016 at 20:57 .\" Subject: Re: futex: Allow FUTEX_CLOCK_REALTIME with FUTEX_WAIT op .\" .\" On Thu, 23 Jun 2016, Michael Kerrisk (man-pages) wrote: .\" > On 06/23/2016 08:28 PM, Darren Hart wrote: .\" > > And as a follow-on, what is the reason for FUTEX_LOCK_PI only using .\" > > CLOCK_REALTIME? It seems reasonable to me that a user may want to wait a .\" > > specific amount of time, regardless of wall time. .\" > .\" > Yes, that's another weird inconsistency. .\" .\" The reason is that phtread_mutex_timedlock() uses absolute timeouts based on .\" CLOCK_REALTIME. glibc folks asked to make that the default behaviour back .\" then when we added LOCK_PI. Le paramètre \fItimeout\fP fournit un délai de tentative de verrouillage. Si \fItimeout\fP est positif, la structure vers laquelle il pointe indique un délai absolu mesuré en fonction de l'horloge \fBCLOCK_REALTIME\fP. Si \fItimeout\fP est NULL, l'opération se bloquera indéfiniment. .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Les paramètres \fIuaddr2\fP, \fIval\fP et \fIval3\fP sont ignorés. .TP \fBFUTEX_LOCK_PI2\fP (depuis Linux 5.14) .\" commit bf22a6976897977b0a3f1aeba6823c959fc4fdae .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Cette opération est la même que \fBFUTEX_LOCK_PI\fP, sauf que l'horloge par rapport à laquelle \fItimeout\fP est mesuré peut être sélectionnée. Par défaut, le délai (absolu) indiqué dans \fItimeout\fP est mesuré par rapport à l'horloge \fBCLOCK_MONOTONIC\fP mais si l'attribut \fBFUTEX_CLOCK_REALTIME\fP est indiqué dans \fIfutex_op\fP, le délai est mesuré par rapport à l'horloge \fBCLOCK_REALTIME\fP. .TP \fBFUTEX_TRYLOCK_PI\fP (depuis Linux 2.6.18) .\" commit c87e2837be82df479a6bae9f155c43516d2feebc L'opération essaie d'acquérir le verrou sur \fIuaddr\fP. 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 \fB0\fP. .IP .\" Paraphrasing a f2f conversation with Thomas Gleixner about the .\" above point (Aug 2015): ### .\" There is a rare possibility of a race condition involving an .\" uncontended futex with no owner, but with waiters. The .\" kernel-user-space contract is that if a futex is nonzero, you must .\" go into kernel. The futex was owned by a task, and that task dies .\" but there are no waiters, so the futex value is non zero. .\" Therefore, the next locker has to go into the kernel, .\" so that the kernel has a chance to clean up. (CMXCH on zero .\" in user space would fail, so kernel has to clean up.) .\" Darren Hart (Oct 2015): .\" The trylock in the kernel has more state, so it can independently .\" verify the flags that user space must trust implicitly. 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 (\fBFUTEX_WAITERS\fP et/ou \fBFUTEX_OWNER_DIED\fP). 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. .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Les paramètres \fIuaddr2\fP, \fIval\fP, \fItimeout\fP et \fIval3\fP sont ignorés. .TP \fBFUTEX_UNLOCK_PI\fP (depuis Linux 2.6.18) .\" commit c87e2837be82df479a6bae9f155c43516d2feebc Cette opération réveille l'élément ayant la plus haute priorité et attendant un \fBFUTEX_LOCK_PI\fP ou un \fBFUTEX_LOCK_PI2\fP à l'adresse indiquée par le paramètre \fIuaddr\fP. .IP Cela est appelé quand la valeur dans l'espace utilisateur sur \fIuaddr\fP ne peut pas être passée à \fB0\fP de manière atomique depuis un TID (du propriétaire). .IP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Les paramètres \fIuaddr2\fP, \fIval\fP, \fItimeout\fP et \fIval3\fP sont ignorés. .TP \fBFUTEX_CMP_REQUEUE_PI\fP (depuis Linux 2.6.31) .\" commit 52400ba946759af28442dee6265c5c0180ac7122 Cette opération est une variante PI\-aware de \fBFUTEX_CMP_REQUEUE\fP. Elle remet en attente des éléments bloqués avec \fBFUTEX_WAIT_REQUEUE_PI\fP sur \fIuaddr\fP à partir d'un futex source non\-PI (\fIuaddr\fP) vers un futex cible PI (\fIuaddr2\fP). .IP Comme avec \fBFUTEX_CMP_REQUEUE\fP, cette opération réveille un maximum de \fIval\fP éléments qui attendent le futex sur \fIuaddr\fP. Toutefois, pour \fBFUTEX_CMP_REQUEUE_PI\fP, \fIval\fP doit valoir \fB1\fP (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 \fIuaddr\fP et ajoutés sur celle du futex cible sur \fIuaddr2\fP. .IP .\" val2 is the cap on the number of requeued waiters. .\" In the glibc pthread_cond_broadcast() implementation, this argument .\" is specified as INT_MAX, and for pthread_cond_signal() it is 0. .\" .\" The page at http://locklessinc.com/articles/futex_cheat_sheet/ .\" notes that "priority-inheritance Futex to priority-inheritance .\" Futex requeues are currently unsupported". However, probably .\" the page does not need to say nothing about this, since .\" Thomas Gleixner commented (July 2015): "they never will be .\" supported because they make no sense at all" .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Les paramètres \fIval2\fP et \fIval3\fP ont le même objectif qu'avec \fBFUTEX_CMP_REQUEUE\fP. .TP \fBFUTEX_WAIT_REQUEUE_PI\fP (depuis Linux 2.6.31) .\" commit 52400ba946759af28442dee6265c5c0180ac7122 .\" Attendre un futex non\-PI sur \fIuaddr\fP et se mettre potentiellement en attente (avec une opération \fBFUTEX_CMP_REQUEUE_PI\fP dans une autre tâche), d'un futex PI sur \fIuaddr2\fP. L'opération d'attente sur \fIuaddr\fP est la même que pour \fBFUTEX_WAIT\fP. .IP L'élément peut être retiré de la file d'attente sur \fIuaddr\fP sans être transféré sur \fIuaddr2\fP à l’aide d’une opération \fBFUTEX_WAKE\fP dans une autre tâche. Dans ce cas, l'opération \fBFUTEX_WAIT_REQUEUE_PI\fP échoue avec l'erreur \fBEAGAIN\fP. .IP Si \fItimeout\fP n'est pas NULL, la structure vers laquelle il pointe indique un délai absolu de l'opération d'attente. Si \fItimeout\fP est NULL, l'opération peut se bloquer indéfiniment. .IP L'argument \fIval3\fP est ignoré. .IP .\" .\" Darren Hart notes that a patch to allow glibc to fully support .\" PI-aware pthreads condition variables has not yet been accepted into .\" glibc. The story is complex, and can be found at .\" https://sourceware.org/bugzilla/show_bug.cgi?id=11588 .\" Darren notes that in the meantime, the patch is shipped with various .\" PREEMPT_RT-enabled Linux systems. .\" .\" Related to the preceding, Darren proposed that somewhere, man-pages .\" should document the following point: .\" .\" While the Linux kernel, since Linux 2.6.31, supports requeueing of .\" priority-inheritance (PI) aware mutexes via the .\" FUTEX_WAIT_REQUEUE_PI and FUTEX_CMP_REQUEUE_PI futex operations, .\" the glibc implementation does not yet take full advantage of this. .\" Specifically, the condvar internal data lock remains a non-PI aware .\" mutex, regardless of the type of the pthread_mutex associated with .\" the condvar. This can lead to an unbounded priority inversion on .\" the internal data lock even when associating a PI aware .\" pthread_mutex with a condvar during a pthread_cond*_wait .\" operation. For this reason, it is not recommended to rely on .\" priority inheritance when using pthread condition variables. .\" .\" The problem is that the obvious location for this text is .\" the pthread_cond*wait(3) man page. However, such a man page .\" does not currently exist. .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" \fBFUTEX_WAIT_REQUEUE_PI\fP et \fBFUTEX_CMP_REQUEUE_PI\fP 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 \fBFUTEX_WAIT_REQUEUE_PI\fP, l'application dans l'espace utilisateur pré\-indique la cible de la remise en attente qui va se faire dans l'opération \fBFUTEX_CMP_REQUEUE_PI\fP. .SH "VALEUR RENVOYÉE" En cas d'erreur (en supposant que \fBfutex\fP() a été appelé à l’aide de \fBsyscall\fP(2)), toutes les opérations renvoient \fB\-1\fP et positionnent \fIerrno\fP pour indiquer l'erreur. .PP En cas de succès, le code de retour dépend de l'opération, comme décrit dans la liste suivante\ : .TP \fBFUTEX_WAIT\fP Renvoie \fB0\fP 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 \fB0\fP 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. .TP \fBFUTEX_WAKE\fP Renvoie le nombre de processus en attente qui ont été réveillés. .TP \fBFUTEX_FD\fP Renvoie le nouveau descripteur de fichier associé au futex. .TP \fBFUTEX_REQUEUE\fP Renvoie le nombre de processus en attente qui ont été réveillés. .TP \fBFUTEX_CMP_REQUEUE\fP Renvoie le nombre total d'éléments en attente réveillés ou remis dans la file du futex pour le mot futex sur \fIuaddr2\fP. Si cette valeur est supérieure à \fIval\fP, la différence devient le nombre d'éléments en attente remis dans la file du futex pour le mot futex sur \fIuaddr2\fP. .TP \fBFUTEX_WAKE_OP\fP 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 \fIuaddr\fP et \fIuaddr2\fP. .TP \fBFUTEX_WAIT_BITSET\fP Renvoie \fB0\fP si l'appelant a été réveillé. Voir \fBFUTEX_WAIT\fP sur la manière d'interpréter cela correctement en pratique. .TP \fBFUTEX_WAKE_BITSET\fP Renvoie le nombre de processus en attente qui ont été réveillés. .TP \fBFUTEX_LOCK_PI\fP Renvoie \fB0\fP si le futex a appliqué le verrou avec succès. .TP \fBFUTEX_LOCK_PI2\fP Renvoie \fB0\fP si le futex a appliqué le verrou avec succès. .TP \fBFUTEX_TRYLOCK_PI\fP Renvoie \fB0\fP si le futex a appliqué le verrou avec succès. .TP \fBFUTEX_UNLOCK_PI\fP Renvoie \fB0\fP si le futex a correctement enlevé le verrou. .TP \fBFUTEX_CMP_REQUEUE_PI\fP Renvoie le nombre total d'éléments en attente réveillés ou remis dans la file du futex pour le mot futex sur \fIuaddr2\fP. Si cette valeur est supérieure à \fIval\fP, la différence devient le nombre d'éléments en attente remis dans la file du futex pour le mot futex sur \fIuaddr2\fP. .TP \fBFUTEX_WAIT_REQUEUE_PI\fP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" Renvoie \fB0\fP si l'appelant a été mis dans la file d'attente avec succès au futex pour le mot futex sur \fIuaddr2\fP. .SH ERREURS .TP \fBEACCES\fP Pas d'accès en lecture à la mémoire d'un mot futex. .TP \fBEAGAIN\fP (\fBFUTEX_WAIT\fP, \fBFUTEX_WAIT_BITSET\fP, \fBFUTEX_WAIT_REQUEUE_PI\fP) La valeur vers laquelle pointait \fIuaddr\fP n'était pas égale à la valeur \fIval\fP attendue au moment de l'appel. .IP \fBRemarque\fP\ : sur Linux, les noms symboliques \fBEAGAIN\fP et \fBEWOULDBLOCK\fP (les deux apparaissent dans différents endroits du code futex du noyau) ont la même valeur. .TP \fBEAGAIN\fP (\fBFUTEX_CMP_REQUEUE\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) La valeur vers laquelle pointait \fIuaddr\fP n'était pas égale à la valeur \fIval3\fP attendue. .TP \fBEAGAIN\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) L'ID du thread propriétaire du futex sur \fIuaddr\fP (pour \fBFUTEX_CMP_REQUEUE_PI\fP\ : \fIuaddr2\fP) est sur le point de se terminer, mais il n'a pas encore géré le nettoyage de l'état interne. Réessayez. .TP \fBEDEADLK\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) Le mot futex sur \fIuaddr\fP est déjà verrouillé par l'appelant. .TP \fBEDEADLK\fP .\" FIXME . I see that kernel/locking/rtmutex.c uses EDEADLK in some .\" places, and EDEADLOCK in others. On almost all architectures .\" these constants are synonymous. Is there a reason that both .\" names are used? .\" .\" tglx (July 2015): "No. We should probably fix that." .\" (\fBFUTEX_CMP_REQUEUE_PI\fP) Pendant qu'il remettait en attente un élément du PI futex pour le mot futex sur \fIuaddr2\fP, le noyau a détecté un verrou mort (deadlock). .TP \fBEFAULT\fP Le paramètre d'un pointeur nécessaire (c'est\-à\-dire \fIuaddr\fP, \fIuaddr2\fP ou \fItimeout\fP) ne pointait pas vers une adresse valable de l'espace utilisateur. .TP \fBEINTR\fP Une opération \fBFUTEX_WAIT\fP ou \fBFUTEX_WAIT_BITSET\fP a été interrompue par un signal (voir \fBsignal\fP(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. .TP \fBEINVAL\fP L'opération dans \fIfutex_op\fP fait partie de celles qui utilisent un délai, mais le paramètre \fItimeout\fP fourni n'était pas valable (\fItv_sec\fP valait moins de \fB0\fP ou \fItv_nsec\fP ne valait pas moins de 1\ 000\ 000\ 000). .TP \fBEINVAL\fP L'opération indiquée dans \fIfutex_op\fP utilise \fIuaddr\fP et/ou \fIuaddr2\fP 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. .TP \fBEINVAL\fP (\fBFUTEX_WAIT_BITSET\fP, \fBFUTEX_WAKE_BITSET\fP) Le masque de bit fourni dans \fIval3\fP vaut zéro. .TP \fBEINVAL\fP (\fBFUTEX_CMP_REQUEUE_PI\fP) \fIuaddr\fP est égal à \fIuaddr2\fP (c'est\-à\-dire qu'une remise en attente a été tentée sur le même futex). .TP \fBEINVAL\fP (\fBFUTEX_FD\fP) Le numéro du signal fourni dans \fIval\fP n'est pas valable. .TP \fBEINVAL\fP (\fBFUTEX_WAKE\fP, \fBFUTEX_WAKE_OP\fP, \fBFUTEX_WAKE_BITSET\fP, \fBFUTEX_REQUEUE\fP, \fBFUTEX_CMP_REQUEUE\fP) Le noyau a détecté une incohérence entre l'état de l'espace utilisateur sur \fIuaddr\fP et l'état du noyau —\ c'est\-à\-dire qu'il a détecté un élément qui attend dans \fBFUTEX_LOCK_PI\fP ou \fBFUTEX_LOCK_PI2\fP sur \fIuaddr\fP. .TP \fBEINVAL\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_UNLOCK_PI\fP) Le noyau a détecté une incohérence entre l'état de l'espace utilisateur sur \fIuaddr\fP et l'état du noyau. Cela indique soit une corruption d'état, soit que le noyau a trouvé un élément en attente sur \fIuaddr\fP qui attend aussi à l'aide de \fBFUTEX_WAIT\fP ou de \fBFUTEX_WAIT_BITSET\fP. .TP \fBEINVAL\fP .\" From a conversation with Thomas Gleixner (Aug 2015): ### .\" The kernel sees: I have non PI state for a futex you tried to .\" tell me was PI (\fBFUTEX_CMP_REQUEUE_PI\fP) Le noyau a détecté une incohérence entre l'état de l'espace utilisateur sur \fIuaddr\fP et l'état du noyau\ ; c'est\-à\-dire qu'il a détecté un élément qui attend via \fBFUTEX_WAIT\fP ou \fBFUTEX_WAIT_BITSET\fP sur \fIuaddr2\fP. .TP \fBEINVAL\fP (\fBFUTEX_CMP_REQUEUE_PI\fP) Le noyau a détecté une incohérence entre l'état de l'espace utilisateur sur \fIuaddr\fP et l'état du noyau\ ; c'est\-à\-dire qu'il a détecté un élément qui attend à l'aide de \fBFUTEX_WAIT\fP ou de \fBFUTEX_WAIT_BITESET\fP sur \fIuaddr\fP. .TP \fBEINVAL\fP (\fBFUTEX_CMP_REQUEUE_PI\fP) Le noyau a détecté une incohérence entre l'état de l'espace utilisateur sur \fIuaddr\fP et l'état du noyau\ ; c'est\-à\-dire qu'il a détecté un élément qui attend à l'aide de \fBFUTEX_LOCK_PI\fP ou de \fBFUTEX_LOCK_PI2\fP (au lieu de \fBFUTEX_WAIT_REQUEUE_PI\fP). .TP \fBEINVAL\fP .\" This deals with the case: .\" wait_requeue_pi(A, B); .\" requeue_pi(A, C); (\fBFUTEX_CMP_REQUEUE_PI\fP) Tentative de remise dans la file d'un élément en attente vers un futex différent de celui indiqué avec l'appel \fBFUTEX_WAIT_REQUEUE_PI\fP correspondant pour cet élément. .TP \fBEINVAL\fP (\fBFUTEX_CMP_REQUEUE_PI\fP) Le paramètre \fIval\fP ne vaut pas \fB1\fP. .TP \fBEINVAL\fP Argument incorrect. .TP \fBENFILE\fP (\fBFUTEX_FD\fP) La limite du nombre total de fichiers ouverts sur le système a été atteinte. .TP \fBENOMEM\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) Le noyau n'a pas pu allouer de la mémoire pour conserver les informations d'état. .TP \fBENOSYS\fP Opération non valable indiquée dans \fIfutex_op\fP. .TP \fBENOSYS\fP L'option \fBFUTEX_CLOCK_REALTIME\fP était indiquée dans \fIfutex_op\fP, mais l'opération qui l'accompagne n'est ni \fBFUTEX_WAIT\fP, ni \fBFUTEX_WAIT_BITSET\fP, ni \fBFUTEX_WAIT_REQUEUE_PI\fP, ni \fBFUTEX_LOCK_PI2\fP. .TP \fBENOSYS\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_UNLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP, \fBFUTEX_WAIT_REQUEUE_PI\fP) 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. .TP \fBEPERM\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) L'appelant n'est pas autorisé à se rattacher au futex sur \fIuaddr\fP (pour \fBFUTEX_CMP_REQUEUE_PI\fP\ : le futex sur \fIuaddr2\fP) (cela peut venir d'une corruption de l'état dans l'espace utilisateur). .TP \fBEPERM\fP (\fBFUTEX_UNLOCK_PI\fP) Le verrou représenté par le mot futex n'appartient pas à l'appelant. .TP \fBESRCH\fP (\fBFUTEX_LOCK_PI\fP, \fBFUTEX_LOCK_PI2\fP, \fBFUTEX_TRYLOCK_PI\fP, \fBFUTEX_CMP_REQUEUE_PI\fP) L'ID du thread dans le mot futex sur \fIuaddr\fP n'existe pas. .TP \fBESRCH\fP (\fBFUTEX_CMP_REQUEUE_PI\fP) L'ID du thread dans le mot futex sur \fIuaddr2\fP n'existe pas. .TP \fBETIMEDOUT\fP .\" .\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .\" L'opération de \fIfutex_op\fP a utilisé un délai indiqué dans \fItimeout\fP et le délai a expiré avant la fin de l'opération. .SH STANDARDS Linux. .SH HISTORIQUE Linux 2.6.0. .PP 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\(hydessus. 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. .SH 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 \fInloops\fP (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\ : .PP .in +4n .EX $ \fB./futex_demo\fP 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 .EE .in .SS "Source du programme" .\" SRC BEGIN (futex.c) \& .EX /* 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 \[aq]num\-loops\[aq] sur le terminal et ils utilisent un protocole de synchronisation qui garantit qu'ils alternent l'écriture des messages. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include \& static uint32_t *futex1, *futex2, *iaddr; \& static int futex(uint32_t *uaddr, int futex_op, uint32_t val, const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3) { return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3); } \& /* Acquérir le futex vers lequel pointe \[aq]futexp\[aq] : 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 \[aq]futexp\[aq]\ : 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\en", (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\en", (intmax_t) getpid(), j); fpost(futex1); } \& wait(NULL); \& exit(EXIT_SUCCESS); } .EE .\" SRC END .SH "VOIR AUSSI" .ad l \fBget_robust_list\fP(2), \fBrestart_syscall\fP(2), \fBpthread_mutexattr_getprotocol\fP(3), \fBfutex\fP(7), \fBsched\fP(7) .PP Les fichiers suivants des sources du noyau\ : .IP \- 3 \fIDocumentation/pi\-futex.txt\fP .IP \- \fIDocumentation/futex\-requeue\-pi.txt\fP .IP \- \fIDocumentation/locking/rt\-mutex.txt\fP .IP \- \fIDocumentation/locking/rt\-mutex\-design.txt\fP .IP \- \fIDocumentation/robust\-futex\-ABI.txt\fP .PP Franke, H., Russell, R., and Kirwood, M., 2002. \fIFuss, Futexes and Furwocks: Fast Userlevel Locking in Linux\fP (à partir des actions d'Ottawa Linux Symposium 2002), .br .UR http://kernel.org\:/doc\:/ols\:/2002\:/ols2002\-pages\-479\-495.pdf .UE .PP Hart, D., 2009. \fIA futex overview and update\fP, .UR http://lwn.net/Articles/360699/ .UE .PP Hart, D. et Guniguntala, D., 2009. \fIRequeue\-PI: Making Glibc Condvars PI\-Aware\fP (à partir des comptes rendus de l'atelier Real\-Time Linux 2009), .UR http://lwn.net/images/conf/rtlws11/papers/proc/p10.pdf .UE .PP Drepper, U., 2011. \fIFutexes Are Tricky\fP, .UR http://www.akkadia.org/drepper/futex.pdf .UE .PP La bibliothèque d'exemples de futex, futex\-*.tar.bz2 à .br .UR https://mirrors.kernel.org\:/pub\:/linux\:/kernel\:/people\:/rusty/ .UE .\" .\" FIXME(Torvald) We should probably refer to the glibc code here, in .\" particular the glibc-internal futex wrapper functions that are .\" WIP, and the generic pthread_mutex_t and perhaps condvar .\" implementations. .PP .SH TRADUCTION La traduction française de cette page de manuel a été créée par Christophe Blaess , Stéphan Rafin , Thierry Vignaud , François Micaux, Alain Portal , Jean-Philippe Guérard , Jean-Luc Coulon (f5ibh) , Julien Cristau , Thomas Huriaux , Nicolas François , Florentin Duneau , Simon Paillard , Denis Barbier , David Prévot et Jean-Philippe MENGUAL . .PP Cette traduction est une documentation libre ; veuillez vous reporter à la .UR https://www.gnu.org/licenses/gpl-3.0.html GNU General Public License version 3 .UE concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE. .PP Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à .MT debian-l10n-french@lists.debian.org .ME .