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  ⟨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 ⟨⟩.