Skip to content
/linux-syscalls

IPC · Section 2

futex(2)

Mutex utilisateur rapide — la primitive noyau derrière chaque mutex pthread, variable de condition et verrou moderne sous Linux.

Signature

#include <linux/futex.h>
#include <sys/syscall.h>

long futex(uint32_t * uaddr, int futex_op, uint32_t val, const struct timespec * timeout, uint32_t * uaddr2, uint32_t val3);
uaddr
Pointeur sur le mot futex (uint32_t) dans la mémoire utilisateur du processus.
futex_op
Code d'opération (voir le tableau des drapeaux). Les 7 bits bas choisissent l'opération ; les bits hauts portent FUTEX_PRIVATE_FLAG / FUTEX_CLOCK_REALTIME.
val
Valeur propre à l'opération. Pour FUTEX_WAIT, c'est la valeur que uaddr doit avoir pour que l'attente prenne effet ; pour FUTEX_WAKE, le nombre maximum de threads à réveiller.
timeout
Pour les opérations WAIT, timeout relatif ou absolu. NULL signifie attendre indéfiniment.
uaddr2
Pour les opérations de requeue (FUTEX_REQUEUE, FUTEX_CMP_REQUEUE), adresse du futex secondaire. Sinon inutilisé.
val3
Propre à l'opération. Pour FUTEX_WAIT_BITSET / FUTEX_WAKE_BITSET porte le masque de bits pour réveil sélectif.

Description

futex() est la brique de toute primitive de synchronisation utilisateur sous Linux. L'idée : l'état du verrou (un uint32_t à uaddr) est lu et modifié par l'application en espace utilisateur via des instructions atomiques ; le noyau n'est invoqué que pour le chemin lent — quand un thread doit dormir en attente, ou en réveiller un autre. FUTEX_WAIT vérifie atomiquement que *uaddr vaut toujours val et, si oui, bloque le thread appelant ; FUTEX_WAKE réveille jusqu'à val threads en attente sur uaddr. FUTEX_PRIVATE_FLAG marque un futex comme privé au processus (le noyau saute la traduction d'adresse inter-processus), utilisé par défaut par toutes les implémentations pthread modernes. De nombreuses opérations existent (REQUEUE pour le broadcast de condvar, LOCK_PI / UNLOCK_PI pour les mutex à héritage de priorité, WAIT_BITSET pour les réveils sélectifs, FUTEX_WAKE_OP pour les opérations composées atomiques) ; voir futex(2) pour l'ensemble complet.

Numéros par architecture

ArchitectureNuméroABIPoint d'entrée
x86 (i386)240i386sys_futex_time32
x64 (x86_64)202commonsys_futex
ARM64 (aarch64)98——

Historique noyau

Introduit dans Linux 2.5.7.

  1. 2.5.7

    futex() a été ajouté en 2.5.7 (sorti en 2.6) pour remplacer les anciens appels sémaphores lourds. C'est la primitive de verrouillage la plus marquante du Linux moderne, rendant les mutex utilisateur essentiellement gratuits dans le cas non contenté.

  2. 2.6.7

    Le support des futex robustes a été ajouté : set_robust_list / get_robust_list laissent un thread enregistrer une liste par thread des mutex qu'il détient. Si le thread meurt (ou son processus est tué) en pleine section, le noyau parcourt la liste et marque chacun owner-died, laissant les autres détecter et récupérer.

  3. 2.6.18

    Les opérations PI (FUTEX_LOCK_PI / FUTEX_UNLOCK_PI / FUTEX_TRYLOCK_PI) ont été ajoutées pour les threads temps réel — le noyau booste la priorité d'ordonnancement du détenteur quand un attendant de plus haute priorité bloque, évitant les inversions classiques.

  4. 5.16

    futex_waitv() est arrivé : attendre sur plusieurs futex simultanément et se réveiller sur le premier prêt — utilisé par les émulateurs et moteurs de jeux pour des attentes multi-événements efficaces sans les acrobaties FUTEX_REQUEUE.

seccomp & conteneurs

Docker default profile

Autorisé

Podman default profile

Autorisé

futex() est dans tous les profils par défaut et ne peut être bloqué : chaque appel pthread l'utilise. Le plus récent futex_waitv() (Linux 5.16) est aussi fondamental et doit être autorisé. Pas de filtrage d'arguments utile — les codes d'op et adresses sont applicatifs.

libseccomp

seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex),    0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex_waitv), 0);

Exemple strace

$ strace -e futex -p $(pidof firefox) 2>&1 | head -5
futex(0x7f3a14002910, FUTEX_WAIT_PRIVATE, 2, NULL) = 0
futex(0x7f3a14002914, FUTEX_WAKE_PRIVATE, 1) = 1
futex(0x55d4e8c1d0a0, FUTEX_WAIT_BITSET_PRIVATE|FUTEX_CLOCK_REALTIME, 0, {tv_sec=…}, FUTEX_BITSET_MATCH_ANY) = -1 ETIMEDOUT

Les lignes futex() dominent toute strace d'un programme multithreadé — utiliser le mode résumé -c pour voir si la contention domine le temps. Un programme « bloqué » montre souvent un thread en FUTEX_WAIT sans FUTEX_WAKE correspondant ; comparer l'uaddr à /proc/<pid>/maps et la pile du thread détenteur (gdb) identifie l'interblocage. Filtrer avec -e trace=futex pour ne voir que le motif de contention.

Sécurité & observabilité

futex() n'est généralement pas une primitive de sécurité — son universalité fait que son absence est plus intéressante que sa présence. Cela dit, les CVE noyau dans l'implémentation futex ont eu un impact historique (le célèbre pivot Dirty COW utilisait la sémantique futex, et la course PI CVE-2014-3153 a donné root sur Android pendant des années), donc la posture de version noyau compte. Le tracepoint eBPF sys_enter_futex est trop bruyant pour la supervision générale — la plupart des charges en émettent des milliers par seconde. Pour le débogage pthread, l'approche utilisateur (mode debug des mutex glibc, ou rr) est plus productive que la trace syscall.

Erreurs

EACCES
—
EAGAIN
FUTEX_WAIT : *uaddr n'était pas égal à val au moment du contrôle (un wake est survenu juste avant ; le chemin utilisateur doit réessayer).
EFAULT
uaddr hors de l'espace d'adressage du processus.
EINTR
Attente interrompue par un signal. Réessayer côté utilisateur.
EINVAL
Mauvaise opération, uaddr mal aligné (doit être aligné 4 octets), ou combinaison de drapeaux invalide.
ENOSYS
—
ETIMEDOUT
FUTEX_WAIT avec timeout écoulé avant réveil.

Drapeaux

FUTEX_WAIT
0
Vérifie atomiquement *uaddr == val et, si oui, dort jusqu'à réveil (ou timeout). Le chemin de contention de pthread_mutex_lock().
FUTEX_WAKE
1
Réveille jusqu'à val threads en attente sur uaddr. Le chemin de réveil de pthread_mutex_unlock().
FUTEX_REQUEUE
3
Réveille jusqu'à val attendants, puis déplace jusqu'à val2 (slot timeout) autres attendants de uaddr vers uaddr2. Le chemin broadcast de pthread_cond_broadcast().
FUTEX_CMP_REQUEUE
4
—
FUTEX_WAKE_OP
5
—
FUTEX_LOCK_PI
6
Variante héritage de priorité — quand un thread haute priorité se bloque sur un futex tenu par un thread basse priorité, le noyau booste temporairement la priorité du détenteur pour éviter l'inversion.
FUTEX_UNLOCK_PI
7
—
FUTEX_TRYLOCK_PI
8
—
FUTEX_WAIT_BITSET
9
—
FUTEX_WAKE_BITSET
10
—
FUTEX_PRIVATE_FLAG
128
Optimisation processus-privé — évite la résolution d'adresse dans le hash global futex du noyau, utilisé par toute glibc moderne.
FUTEX_CLOCK_REALTIME
256
—

Syscalls liés