Man page - membarrier(2)

Packages contains this manual

Available languages:

en fr ru

Manual

membarrier

NOM
BIBLIOTHÈQUE
SYNOPSIS
DESCRIPTION
VALEUR RENVOYÉE
ERREURS
STANDARDS
HISTORIQUE
NOTES
EXEMPLES
TRADUCTION

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

Image grohtml-3856560-1.png

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 .