membarrier(2) System Calls Manual membarrier(2) NOM membarrier - Poser des barrieres memoire sur un ensemble de threads BIBLIOTHEQUE Bibliotheque C standard (libc, -lc) SYNOPSIS #include /* Definition des constantes MEMBARRIER_* */ #include /* Definition des constantes SYS_* */ #include int syscall(SYS_membarrier, int cmd, unsigned int flags, int cpu_id); Note : la glibc ne fournit pas de fonction d'enveloppe pour membarrier(), necessitant l'utilisation de syscall(2). DESCRIPTION L'appel systeme membarrier() aide a reduire le temps-systeme des instructions de barrieres memoire necessaire pour organiser les acces a la memoire sur des systemes a plusieurs coeurs. Cependant, cet appel systeme est plus lourd qu'une barriere memoire, donc l'utiliser efficacement << n'est pas >> aussi simple que de remplacer une barriere memoire par cet appel systeme, mais necessite de comprendre les details ci-dessous. L'utilisation de barrieres memoire doit se faire en tenant compte du fait qu'elles doivent soit etre associees avec leurs homologues, ou que le modele de memoire de l'architecture n'a pas besoin de barrieres associees. Dans certains cas, une face des barrieres associees (qu'on appellera la << face rapide >>) est sollicitee beaucoup plus souvent que l'autre (qu'on appellera la << face lente >>). C'est le motif principal pour utiliser membarrier(). L'idee cle est de remplacer, pour ces barrieres associees, les barrieres memoire de la face rapide par de simples barrieres du compilateur, par exemple : asm volatile ("" : : : "memory") et de remplacer les barrieres memoire de la face lente par des appels a membarrier(). Cela ajoutera du temps-systeme a la face lente et en supprimera de la face rapide, d'ou une augmentation globale de performances tant que la face lente est si peu utilisee que le temps-systeme d'appels membarrier() ne l'emporte pas sur le gain de performance de la face rapide. Le parametre cmd est l'un des suivants : MEMBARRIER_CMD_QUERY (depuis Linux 4.3) Rechercher l'ensemble des commandes prises en charge. Le code de retour de l'appel est un masque de bits des commandes prises en charge. MEMBARRIER_CMD_QUERY, dont la valeur est 0, n'est pas inclus dans ce masque de bits. Cette commande est toujours prise en charge (sur les noyaux ou membarrier() est fourni). MEMBARRIER_CMD_GLOBAL (depuis Linux 4.16) S'assurer que tous les threads de tous les processus du systeme passent par un etat ou tous les acces memoire aux adresses de l'espace utilisateur correspondent a l'organisation du programme entre l'entree et le retour de l'appel systeme membarrier(). Tous les threads du systeme sont vises par cette commande. MEMBARRIER_CMD_GLOBAL_EXPEDITED (depuis Linux 4.16) Mettre une barriere memoire sur tous les threads en cours de tous les processus qui se sont enregistres precedemment avec MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED. Lors du retour de l'appel systeme, le thread appelant a la garantie que tous les threads en cours sont passes par un etat ou tous les acces memoire aux adresses de l'espace utilisateur correspondent a l'organisation du programme entre l'entree et le retour de l'appel systeme (les threads non en cours sont dans cet etat de facto). Cette garantie n'est apportee qu'aux threads des processus qui se sont precedemment enregistres avec MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED. Etant donne que l'enregistrement concerne l'intention de recevoir des barrieres, il est possible d'appeler MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED a partir d'un processus qui n'a pas utilise MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED. Les commandes << accelerees >> (expedited) se terminent plus vite que celles non accelerees ; elles ne se bloquent jamais, mais elles causent aussi un temps-systeme supplementaire. MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED (depuis Linux 4.16) Enregistrer l'intention du processus de recevoir des barrieres memoire MEMBARRIER_CMD_GLOBAL_EXPEDITED. MEMBARRIER_CMD_PRIVATE_EXPEDITED (depuis Linux 4.14) Poser une barriere memoire sur chaque thread en cours appartenant au meme processus que le thread appelant. Au retour de l'appel systeme, le thread appelant a la garantie que tous ses homologues en cours passent par un etat ou tous les acces memoire aux adresses de l'espace utilisateur correspondent a l'ordre du programme entre l'entree et le retour de l'appel systeme (les threads non en cours sont dans cet etat de facto). Cette garantie n'est apportee qu'aux threads du meme processus que le thread appelant. Les commandes << accelerees >> (expedited) se terminent plus vite que celles non accelerees ; elles ne se bloquent jamais, mais elles causent aussi un temps-systeme supplementaire. Un processus doit enregistrer son intention d'utiliser la commande acceleree privee avant de le faire. MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED (depuis Linux 4.14) Enregistrer l'intention du processus d'utiliser MEMBARRIER_CMD_PRIVATE_EXPEDITED. MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE (depuis Linux 4.16) Outre les garanties d'organisation de la memoire decrites dans MEMBARRIER_CMD_PRIVATE_EXPEDITED, lors du retour de l'appel systeme, le thread appelant a la garantie que tous ses homologues ont execute une instruction de serialisation du coeur. Cette garantie n'est apportee que pour les threads du meme processus que celui appelant. Les commandes << accelerees >> se terminent plus vite que celles non accelerees, elles ne se bloquent jamais, mais demandent aussi un temps-systeme supplementaire. Un processus doit enregistrer son intention d'utiliser la commande acceleree privee de synchronisation de coeur avant de le faire. MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE (depuis Linux 4.16) Enregistrer l'intention du processus d'utiliser MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE. MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ (depuis Linux 5.10) Assurer au thread appelant, pendant le retour de l'appel systeme, que tous ses homologues en cours ont toutes les sections critiques rseq (restartable sequence) en cours redemarrees si le parametre flags vaut 0 ; s'il vaut MEMBARRIER_CMD_FLAG_CPU, cette operation n'est effectuee que sur le processeur indique par cpu_id. Cette garantie n'est apportee qu'aux threads du meme processus que le thread appelant. RSEQ membarrier n'est disponible que sous la forme << private expedited >>. Un processus doit enregistrer son intention d'utiliser la commande acceleree privee rseq avant de le faire. MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ (depuis Linux 5.10) Enregistrer l'intention du processus d'utiliser MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ. MEMBARRIER_CMD_SHARED (depuis Linux 4.3) Il s'agit d'un alias pour MEMBARRIER_CMD_GLOBAL pour la retrocompatibilite de l'entete. Le parametre flags doit etre indique en tant que 0, sauf si la commande est MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ, auquel cas flags peut etre soit 0, soit MEMBARRIER_CMD_FLAG_CPU. Le parametre cpu_id est ignore sauf si flags est MEMBARRIER_CMD_FLAG_CPU, auquel cas il doit indiquer le processeur cible par cette commande membarrier. Tous les acces memoire effectues dans l'organisation du programme a partir de chaque thread vise sont garantis d'etre organises par rapport a membarrier(). Si nous utilisons la semantique barrier() pour representer une barriere du compilateur qui force les acces memoire a s'operer dans l'ordre du programme le long des barrieres, et smp_mb() pour representer les barrieres explicites de la memoire qui forcent toute la memoire a s'organiser le long de la barriere, nous obtenons le tableau d'organisation suivant pour chaque paire de barrier(), membarrier() et smp_mb(). L'organisation de la paire est detaillee ainsi (O : organisee, X : non organisee) : barrier() smp_mb() membarrier() barrier() X X O smp_mb() X O O membarrier() O O O VALEUR RENVOYEE En cas de succes, l'operation MEMBARRIER_CMD_QUERY renvoie un masque de bits des commandes prises en charge, et les operations MEMBARRIER_CMD_GLOBAL, MEMBARRIER_CMD_GLOBAL_EXPEDITED, MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED, MEMBARRIER_CMD_PRIVATE_EXPEDITED, MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE et MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE renvoient 0. En cas d'erreur, -1 est renvoye et errno est defini pour indiquer l'erreur. Pour une commande donnee, quand flags est positionne sur 0, cet appel systeme est garanti de renvoyer toujours la meme valeur jusqu'au redemarrage. Les appels suivants ayant les memes parametres conduiront au meme resultat. Donc, quand flags est positionne sur 0, une gestion des erreurs n'est necessaire que pour le premier appel a membarrier(). ERREURS EINVAL cmd n'est pas valable ou flags ne vaut pas zero ou la commande MEMBARRIER_CMD_GLOBAL est desactivee car le parametre nohz_full du processeur a ete positionne ou les commandes MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE et MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE ne sont pas implementees par l'architecture. ENOSYS L'appel systeme membarrier() n'est pas implemente par ce noyau. EPERM Le processus actuel n'etait pas enregistre avant d'utiliser les commandes accelerees privees. STANDARDS Linux. HISTORIQUE Linux 4.3. Avant Linux 5.10, le prototype etait : int membarrier(int cmd, int flags); NOTES Une instruction de barriere memoire fait partie du jeu d'instructions des architectures ayant des modeles de memoire faiblement organises. Elle organise les acces memoire avant et apres la barriere par rapport a celles correspondantes sur les autres coeurs. Par exemple, une barriere de charge peut organiser les charges avant et apres elle par rapport aux stockages conserves dans les barrieres de stockage. L'organisation du programme est l'ordre dans lequel les instructions sont ordonnees dans le code d'assembleur du programme. Parmi les exemples ou membarrier() peut etre utile, figurent les implementations des bibliotheques Read-Copy-Update et des ramasse-miettes EXEMPLES Supposons une application multithreadee ou << fast_path() >> est executee tres souvent et ou << slow_path() >> l'est rarement, alors le code suivant (x86) peut etre transforme en utilisant membarrier() : #include static volatile int a, b; static void fast_path(int *read_b) { a = 1; asm volatile ("mfence" : : : "memory"); *read_b = b; } static void slow_path(int *read_a) { b = 1; asm volatile ("mfence" : : : "memory"); *read_a = a; } int main(void) { int read_a, read_b; /* * De vraies applications appelleraient fast_path() et slow_path() * a partir de differents threads. Appel a partir de main() * pour garder cet exemple court. */ slow_path(&read_a); fast_path(&read_b); /* * read_b == 0 implique que read_a == 1 et * read_a == 0 implique que read_b == 1. */ if (read_b == 0 && read_a == 0) abort(); exit(EXIT_SUCCESS); } Le code ci-dessus transforme pour utiliser membarrier() donne : #define _GNU_SOURCE #include #include #include #include #include static volatile int a, b; static int membarrier(int cmd, unsigned int flags, int cpu_id) { return syscall(__NR_membarrier, cmd, flags, cpu_id); } static int init_membarrier(void) { int ret; /* Verifier que membarrier() est pris en charge. */ ret = membarrier(MEMBARRIER_CMD_QUERY, 0, 0); if (ret < 0) { perror("membarrier"); return -1; } if (!(ret & MEMBARRIER_CMD_GLOBAL)) { fprintf(stderr, "membarrier ne gere pas MEMBARRIER_CMD_GLOBAL\n"); return -1; } return 0; } static void fast_path(int *read_b) { a = 1; asm volatile ("" : : : "memory"); *read_b = b; } static void slow_path(int *read_a) { b = 1; membarrier(MEMBARRIER_CMD_GLOBAL, 0, 0); *read_a = a; } int main(int argc, char *argv[]) { int read_a, read_b; if (init_membarrier()) exit(EXIT_FAILURE); /* * De vraies applications appelleraient fast_path() et slow_path() * a partir de differents threads. Appel a partir de main() * pour garder cet exemple court. */ slow_path(&read_a); fast_path(&read_b); /* * read_b == 0 implique que read_a == 1 et * read_a == 0 implique que read_b == 1. */ if (read_b == 0 && read_a == 0) abort(); exit(EXIT_SUCCESS); } TRADUCTION La traduction francaise de cette page de manuel a ete creee par Christophe Blaess , Stephan Rafin , Thierry Vignaud , Francois Micaux, Alain Portal , Jean-Philippe Guerard , Jean-Luc Coulon (f5ibh) , Julien Cristau , Thomas Huriaux , Nicolas Francois , Florentin Duneau , Simon Paillard , Denis Barbier , David Prevot et Jean-Philippe MENGUAL Cette traduction est une documentation libre ; veuillez vous reporter a la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n'y a aucune RESPONSABILITE LEGALE. Si vous decouvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message a . Pages du manuel de Linux 6.06 31 octobre 2023 membarrier(2)