Provided by: manpages-fr-dev_4.19.0-7_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.

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