dwww Home | Manual pages | Find package

membarrier(2)                 System Calls Manual                membarrier(2)

NOM
       membarrier - Poser des barrières mémoire sur un ensemble de threads

BIBLIOTHÈQUE
       Bibliothèque C standard (libc, -lc)

SYNOPSIS
       #include <linux/membarrier.h> /* Définition des constantes MEMBARRIER_* */
       #include <sys/syscall.h>      /* Définition des constantes SYS_* */
       #include <unistd.h>

       int syscall(SYS_membarrier, int cmd, unsigned int flags, int cpu_id);

       Note :  la  glibc  ne  fournit pas de fonction d'enveloppe pour membar-
       rier(), nécessitant l'utilisation de syscall(2).

DESCRIPTION
       L'appel système membarrier() aide à réduire le temps-système  des  ins-
       tructions de barrières mémoire nécessaire pour organiser les accès à la
       mémoire sur des systèmes à plusieurs cœurs. Cependant, cet  appel  sys-
       tème  est plus lourd qu'une barrière mémoire, donc l'utiliser efficace-
       ment « n'est pas » aussi simple que de remplacer une  barrière  mémoire
       par cet appel système, mais nécessite de comprendre les détails ci-des-
       sous.

       L'utilisation de barrières mémoire doit se faire en  tenant  compte  du
       fait qu'elles doivent soit être associées avec leurs homologues, ou que
       le modèle de mémoire de l'architecture n'a pas besoin de barrières  as-
       sociées.

       Dans certains cas, une face des barrières associées (qu'on appellera la
       « face rapide ») est  sollicitée  beaucoup  plus  souvent  que  l'autre
       (qu'on appellera la « face lente »). C'est le motif principal pour uti-
       liser membarrier(). L'idée clé est de remplacer, pour ces barrières as-
       sociées,  les  barrières  mémoire de la face rapide par de simples bar-
       rières du compilateur, par exemple :

           asm volatile ("" : : : "memory")

       et de remplacer les barrières mémoire de la face lente par des appels à
       membarrier().

       Cela  ajoutera  du temps-système à la face lente et en supprimera de la
       face rapide, d'où une augmentation globale de performances tant que  la
       face  lente  est  si peu utilisée que le temps-système d'appels membar-
       rier() ne l’emporte pas sur le gain de performance de la face rapide.

       Le paramètre 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 où membarrier() est fourni).

       MEMBARRIER_CMD_GLOBAL (depuis Linux 4.16)
              S'assurer que tous les threads de tous les processus du  système
              passent  par  un  état où tous les accès mémoire aux adresses de
              l'espace utilisateur correspondent à l'organisation du programme
              entre  l'entrée  et  le  retour de l'appel système membarrier().
              Tous les threads du système sont visés par cette commande.

       MEMBARRIER_CMD_GLOBAL_EXPEDITED (depuis Linux 4.16)
              Mettre une barrière mémoire sur tous les  threads  en  cours  de
              tous  les  processus  qui  se sont enregistrés précédemment avec
              MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED.

              Lors du retour de l'appel système, le thread appelant a  la  ga-
              rantie  que tous les threads en cours sont passés par un état où
              tous les accès mémoire aux adresses de l'espace utilisateur cor-
              respondent  à  l'organisation  du programme entre l'entrée et le
              retour de l'appel système (les threads non en  cours  sont  dans
              cet état de facto). Cette garantie n'est apportée qu'aux threads
              des processus qui se sont précédemment enregistrés avec  MEMBAR-
              RIER_CMD_REGISTER_GLOBAL_EXPEDITED.

              Étant  donné  que l'enregistrement concerne l'intention de rece-
              voir des barrières, il est possible d'appeler MEMBARRIER_CMD_RE-
              GISTER_GLOBAL_EXPEDITED à partir d’un processus qui n’a pas uti-
              lisé MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED.

              Les commandes « accélérées » (expedited) se terminent plus  vite
              que  celles  non  accélérées ; elles ne se bloquent jamais, mais
              elles causent aussi un temps-système supplémentaire.

       MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED (depuis Linux 4.16)
              Enregistrer l'intention du processus de recevoir  des  barrières
              mémoire MEMBARRIER_CMD_GLOBAL_EXPEDITED.

       MEMBARRIER_CMD_PRIVATE_EXPEDITED (depuis Linux 4.14)
              Poser  une  barrière mémoire sur chaque thread en cours apparte-
              nant au même processus que le thread appelant.

              Au retour de l'appel système, le thread appelant a  la  garantie
              que tous ses homologues en cours passent par un état où tous les
              accès mémoire aux adresses de l'espace utilisateur correspondent
              à  l'ordre  du  programme entre l'entrée et le retour de l'appel
              système (les threads non en cours sont dans cet état de  facto).
              Cette  garantie  n'est apportée qu'aux threads du même processus
              que le thread appelant.

              Les commandes « accélérées » (expedited) se terminent plus  vite
              que  celles  non  accélérées ; elles ne se bloquent jamais, mais
              elles causent aussi un temps-système supplémentaire.

              Un processus doit enregistrer son intention d'utiliser  la  com-
              mande accélérée privée avant de le faire.

       MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED (depuis Linux 4.14)
              Enregistrer   l'intention   du   processus   d'utiliser  MEMBAR-
              RIER_CMD_PRIVATE_EXPEDITED.

       MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE (depuis Linux 4.16)
              Outre les garanties d'organisation de la mémoire  décrites  dans
              MEMBARRIER_CMD_PRIVATE_EXPEDITED, lors du retour de l'appel sys-
              tème, le thread appelant a la garantie que tous  ses  homologues
              ont  exécuté une instruction de sérialisation du cœur. Cette ga-
              rantie n'est apportée que pour les threads du même processus que
              celui appelant.

              Les  commandes  « accélérées » se terminent plus vite que celles
              non accélérées, elles ne  se  bloquent  jamais,  mais  demandent
              aussi un temps-système supplémentaire.

              Un  processus  doit enregistrer son intention d'utiliser la com-
              mande accélérée privée de synchronisation de cœur  avant  de  le
              faire.

       MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE (depuis Linux 4.16)
              Enregistrer   l'intention   du   processus   d'utiliser  MEMBAR-
              RIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE.

       MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ (depuis Linux 5.10)
              Assurer au thread appelant, pendant le retour  de  l'appel  sys-
              tème,  que  tous ses homologues en cours ont toutes les sections
              critiques rseq (restartable sequence) en cours redémarrées si le
              paramètre  flags  vaut  0 ;  s'il  vaut MEMBARRIER_CMD_FLAG_CPU,
              cette opération n'est effectuée que sur  le  processeur  indiqué
              par cpu_id. Cette garantie n'est apportée qu'aux threads du même
              processus que le thread appelant.

              RSEQ membarrier n'est disponible que sous la forme « private ex-
              pedited ».

              Un  processus  doit enregistrer son intention d'utiliser la com-
              mande accélérée privée rseq avant de le faire.

       MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ (depuis Linux 5.10)
              Enregistrer  l'intention   du   processus   d'utiliser   MEMBAR-
              RIER_CMD_PRIVATE_EXPEDITED_RSEQ.

       MEMBARRIER_CMD_SHARED (depuis Linux 4.3)
              Il  s'agit  d'un alias pour MEMBARRIER_CMD_GLOBAL pour la rétro-
              compatibilité de l'entête.

       Le paramètre flags doit être indiqué en tant que 0, sauf si la commande
       est  MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ,  auquel cas flags peut être
       soit 0, soit MEMBARRIER_CMD_FLAG_CPU.

       Le  paramètre  cpu_id  est   ignoré   sauf   si   flags   est   MEMBAR-
       RIER_CMD_FLAG_CPU,  auquel cas il doit indiquer le processeur ciblé par
       cette commande membarrier.

       Tous les accès mémoire effectués dans  l'organisation  du  programme  à
       partir de chaque thread visé sont garantis d'être organisés par rapport
       à membarrier().

       Si nous utilisons la sémantique barrier() pour représenter une barrière
       du  compilateur  qui force les accès mémoire à s'opérer dans l'ordre du
       programme le long des barrières, et smp_mb() pour représenter les  bar-
       rières  explicites de la mémoire qui forcent toute la mémoire à s'orga-
       niser le long de la barrière, nous obtenons le  tableau  d'organisation
       suivant pour chaque paire de barrier(), membarrier() et smp_mb(). L'or-
       ganisation de la paire est détaillée ainsi (O : organisée, X : non  or-
       ganisée) :

                             barrier()   smp_mb()   membarrier()
              barrier()          X          X            O
              smp_mb()           X          O            O
              membarrier()       O          O            O

VALEUR RENVOYÉE
       En cas de succès, l'opération MEMBARRIER_CMD_QUERY renvoie un masque de
       bits  des  commandes  prises  en  charge,  et  les  opérations  MEMBAR-
       RIER_CMD_GLOBAL, MEMBARRIER_CMD_GLOBAL_EXPEDITED, MEMBARRIER_CMD_REGIS-
       TER_GLOBAL_EXPEDITED,     MEMBARRIER_CMD_PRIVATE_EXPEDITED,     MEMBAR-
       RIER_CMD_REGISTER_PRIVATE_EXPEDITED,     MEMBARRIER_CMD_PRIVATE_EXPEDI-
       TED_SYNC_CORE  et   MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_SYNC_CORE
       renvoient  0.  En cas d'erreur, -1 est renvoyé et errno est défini pour
       indiquer l'erreur.

       Pour une commande donnée, quand flags est positionné sur 0,  cet  appel
       système  est garanti de renvoyer toujours la même valeur jusqu'au redé-
       marrage. Les appels suivants ayant les mêmes paramètres  conduiront  au
       même  résultat. Donc, quand flags est positionné sur 0, une gestion des
       erreurs n'est nécessaire que pour le premier appel à membarrier().

ERREURS
       EINVAL cmd n'est pas valable ou flags ne vaut pas zéro ou  la  commande
              MEMBARRIER_CMD_GLOBAL  est désactivée car le paramètre nohz_full
              du  processeur  a  été  positionné  ou  les  commandes   MEMBAR-
              RIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE   et  MEMBARRIER_CMD_REGIS-
              TER_PRIVATE_EXPEDITED_SYNC_CORE ne  sont  pas  implémentées  par
              l'architecture.

       ENOSYS L'appel système membarrier() n'est pas implémenté par ce noyau.

       EPERM  Le  processus actuel n'était pas enregistré avant d'utiliser les
              commandes accélérées privées.

VERSIONS
       L'appel système membarrier() a été ajouté dans Linux 4.3.

       Avant Linux 5.10, le prototype de membarrier() était :

           int membarrier(int cmd, int flags);

STANDARDS
       membarrier() est spécifique à Linux.

NOTES
       Une instruction de barrière mémoire fait partie du  jeu  d'instructions
       des  architectures  ayant  des modèles de mémoire faiblement organisés.
       Elle organise les accès mémoire avant et après la barrière par  rapport
       à  celles  correspondantes  sur les autres cœurs. Par exemple, une bar-
       rière de charge peut organiser les charges avant et après elle par rap-
       port aux stockages conservés dans les barrières de stockage.

       L'organisation  du  programme  est l'ordre dans lequel les instructions
       sont ordonnées dans le code d'assembleur du programme.

       Parmi les exemples où membarrier() peut être utile, figurent les implé-
       mentations des bibliothèques Read-Copy-Update et des ramasse-miettes

EXEMPLES
       Supposons une application multithreadée où « fast_path() » est exécutée
       très souvent et où « slow_path() » l'est rarement, alors le  code  sui-
       vant (x86) peut être transformé en utilisant membarrier() :

           #include <stdlib.h>

           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()
                * à partir de différents threads. Appel à 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 transformé pour utiliser membarrier() donne :

           #define _GNU_SOURCE
           #include <stdlib.h>
           #include <stdio.h>
           #include <unistd.h>
           #include <sys/syscall.h>
           #include <linux/membarrier.h>

           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;

               /* Vérifier 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 gère 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()
                * à partir de différents threads. Appel à 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 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  15 décembre 2022                  membarrier(2)

Generated by dwww version 1.15 on Sat Jun 29 01:52:04 CEST 2024.