Provided by: manpages-fr-dev_4.27.0-1_all bug

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 membarrier(), nécessitant l'utilisation de
       syscall(2).

DESCRIPTION

       L'appel système membarrier() aide à réduire  le  temps-système  des  instructions  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
       système  est  plus lourd qu'une barrière mémoire, donc l'utiliser efficacement « 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-dessous.

       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 associé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
       utiliser  membarrier().  L'idée clé est de remplacer, pour ces barrières associées, les barrières mémoire
       de la face rapide par de simples barriè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
       membarrier() 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 garantie 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
              correspondent  à  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 MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED.

              Étant  donné  que l'enregistrement concerne l'intention de recevoir des barrières, il est possible
              d'appeler MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED à partir d’un processus  qui  n’a  pas  utilisé
              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 appartenant 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 commande 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 MEMBARRIER_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 système, le thread appelant a la garantie que tous ses homologues ont
              exécuté une instruction de sérialisation du cœur. Cette  garantie  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  commande  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 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 systè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 expedited ».

              Un  processus doit enregistrer son intention d'utiliser la commande 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 MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ.

       MEMBARRIER_CMD_SHARED (depuis Linux 4.3)
              Il s'agit d'un alias pour MEMBARRIER_CMD_GLOBAL pour la rétrocompatibilité 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 MEMBARRIER_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 barrières explicites de la mémoire qui forcent toute la mémoire à s'organiser le long de la barrière,
       nous obtenons le tableau d'organisation suivant pour chaque paire de barrier(), membarrier() et smp_mb().
       L'organisation de la paire est détaillée ainsi (O : organisée, X : non organisé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      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 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
              MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORE et  MEMBARRIER_CMD_REGISTER_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.

STANDARDS

       Linux.

HISTORIQUE

       Linux 4.3.

       Avant Linux 5.10, le prototype était :

           int membarrier(int cmd, int flags);

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 barrière de charge peut organiser les
       charges avant et après elle par rapport 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  suivant  (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   Christophe   Blaess
       <https://www.blaess.fr/christophe/>,   Stéphan   Rafin   <stephan.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.coulon@wanadoo.fr>,   Julien   Cristau
       <jcristau@debian.org>,     Thomas     Huriaux      <thomas.huriaux@gmail.com>,      Nicolas      François
       <nicolas.francois@centraliens.net>,     Florentin     Duneau    <fduneau@gmail.com>,    Simon    Paillard
       <simon.paillard@resel.enst-bretagne.fr>,    Denis    Barbier    <barbier@debian.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 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.9.1                    15 juin 2024                                     membarrier(2)