Provided by: manpages-fr-dev_4.13-4_all bug

NOM

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

SYNOPSIS

       #include <linux/membarrier.h>

       int membarrier(int cmd, unsigned int flags, int cpu_id);

       Note : il n'existe pas d'enveloppe pour cet appel système dans la glibc ; voir NOTES.

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 positionné adéquatement.

       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.

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);

CONFORMITÉ

       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  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

       La glibc ne fournit pas de fonction autour de cet appel système ; appelez-le avec syscall(2).

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(int argc, char **argv)
           {
               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);
           }

COLOPHON

       Cette page fait partie de la publication 5.10 du projet man-pages Linux. Une description du projet et des
       instructions  pour  signaler  des  anomalies et la dernière version de cette page peuvent être trouvées à
       l'adresse https://www.kernel.org/doc/man-pages/.

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.

Linux                                            1 novembre 2020                                   MEMBARRIER(2)