MMAP(2) | Manuel du programmeur Linux | MMAP(2) |
mmap, munmap - Établir/supprimer une projection en mémoire (map/unmap) des fichiers ou des périphériques
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int munmap(void *addr, size_t length);
Consultez la section NOTES pour plus d'informations sur les exigences de la macro de test de fonctionnalité.
mmap() crée une nouvelle projection dans l'espace d'adressage virtuel du processus appelant. L'adresse de démarrage de la nouvelle projection est indiquée dans addr. Le paramètre length indique la longueur de la projection (qui doit être supérieure à 0).
Si addr est NULL, le noyau choisit l'adresse (alignée sur une page) à laquelle démarrer la projection ; c'est la méthode la plus portable pour créer une nouvelle projection. Si addr n'est pas NULL, le noyau le considère comme une indication sur l'endroit où placer la projection ; sous Linux, elle sera placée à une frontière de page proche (mais toujours supérieure ou égale à la valeur indiquée par /proc/sys/vm/mmap_min_addr) et tente d'y créer la projection. Si une projection y existe déjà, le noyau prend une nouvelle adresse qui peut ou pas dépendre de l'indication. L'adresse de la nouvelle projection est renvoyée comme résultat de l'appel.
Le contenu d'une projection de fichier (par opposition à une projection anonyme ; voir ci-dessous MAP_ANONYMOUS) est initialisé avec length octets à partir de la position offset dans le fichier (ou autre objet) correspondant au descripteur de fichier fd. offset doit être un multiple de la taille de la page renvoyée par sysconf(_SC_PAGE_SIZE).
Après le retour de l'appel mmap(), le descripteur de fichier fd peut être fermé immédiatement sans invalider la projection.
L'argument prot indique la protection que l'on désire pour cette zone de mémoire et ne doit pas entrer en conflit avec le mode d'ouverture du fichier. Il s'agit soit de PROT_NONE (le contenu de la mémoire est inaccessible) soit d'un OU binaire entre les constantes suivantes :
Le paramètre flags détermine si les modifications de la projection sont visibles depuis les autres processus projetant la même région et si les modifications sont appliquées au fichier sous-jacent. Ce comportement est déterminé en incluant exactement une des valeurs suivantes dans flags :
MAP_SHARED et MAP_PRIVATE sont décrits dans POSIX.1-2001 et POSIX.1-2008. MAP_SHARED_VALIDATE est une extension Linux.
De plus, zéro ou plus des valeurs suivantes peuvent être incluses dans flags (avec un OU binaire) :
#define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT) #define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT)
Parmi les attributs ci-dessus, seul MAP_FIXED est spécifié dans POSIX.1-2001 et POSIX.1-2008. Cependant, la plupart des systèmes gèrent aussi MAP_ANONYMOUS (ou son synonyme MAP_ANON).
L'appel système munmap() détruit la projection dans la zone de mémoire spécifiée et s'arrange pour que toute référence ultérieure à cette zone mémoire déclenche une erreur d'adressage. La projection est aussi automatiquement détruite lorsque le processus se termine. À l'inverse, la fermeture du descripteur de fichier ne supprime pas la projection.
L'adresse addr doit être un multiple de la taille de la page (mais ce n'est pas obligatoire pour length). Toutes les pages contenant une partie de l'intervalle indiqué sont libérées, et tout accès ultérieur déclenchera SIGSEGV. Aucune erreur n'est détectée si l'intervalle indiqué ne contient pas de page projetée.
En cas de succès, mmap() renvoie un pointeur sur la zone projetée. En cas d'échec, la valeur MAP_FAILED (c'est-à-dire (void *) -1) est renvoyée et errno est positionné pour indiquer la cause de l'erreur.
S'il réussit, munmap() renvoie 0. En cas d'échec, -1 est renvoyé et errno est positionné pour indiquer la cause de l'erreur (probablement EINVAL).
L'accès à une zone de projection peut déclencher les signaux suivants :
Pour une explication des termes utilisés dans cette section, consulter attributes(7).
Interface | Attribut | Valeur |
mmap(), munmap() | Sécurité des threads | MT-Safe |
POSIX.1-2001, POSIX.1-2008, SVr4, 4.4BSD.
Sur les systèmes POSIX sur lesquels mmap(), msync(2) et munmap() sont disponibles, _POSIX_MAPPED_FILES est définie dans <unistd.h> comme étant une valeur supérieure à 0 (consultez aussi sysconf(3)).
La mémoire obtenue par mmap est préservée au travers d'un fork(2), avec les mêmes attributs.
Un fichier est projeté en multiples de de la taille de la page. Pour un fichier dont la longueur n'est pas un multiple de la taille de page, la mémoire restante est remplie de zéros lors de la projection, et les écritures dans cette zone n'affectent pas le fichier. Les effets de la modification de la taille du fichier sous‐jacent sur les pages correspondant aux zones ajoutées ou supprimées ne sont pas précisés.
Sur certaines architectures matérielles (par exemple, i386), PROT_WRITE implique PROT_READ. Cela dépend de l'architecture si PROT_READ implique PROT_EXEC ou non. Les programmes portables doivent toujours indiquer PROT_EXEC s'ils veulent exécuter du code dans la projection.
La manière portable de créer une projection est de spécifier addr à 0 (NULL), et d'omettre MAP_FIXED dans flags. Dans ce cas, le système choisit l'adresse de la projection ; l'adresse est choisie de manière à ne pas entrer en conflit avec une projection existante et de ne pas être nulle. Si l'attribut MAP_FIXED est indiqué et si addr vaut 0 (NULL), l'adresse projetée sera zéro (NULL).
Certaines constantes de flags sont définies seulement si des macros de test de fonctionnalités adaptées sont définies (potentiellement par défaut) : _DEFAULT_SOURCE avec la glibc 2.19 ou supérieure, ou bien _BSD_SOURCE ou _SVID_SOURCE dans la glibc 2.19 et antérieure (la définition de _GNU_SOURCE suffit également, et son usage aurait été plus logique, puisque ces attributs sont tous spécifiques à Linux). Les attributs adéquats sont : MAP_32BIT, MAP_ANONYMOUS (et son synonyme MAP_ANON), MAP_DENYWRITE, MAP_EXECUTABLE, MAP_FILE, MAP_GROWSDOWN, MAP_HUGETLB, MAP_LOCKED, MAP_NONBLOCK, MAP_NORESERVE, MAP_POPULATE, et MAP_STACK.
Une application peut déterminer les pages d'une projection se trouvant dans le tampon/le cache de page en utilisant mincore(2).
La seule utilisation sûre de MAP_FIXED est quand la plage d'adresses indiquée par addr et length a été préalablement réservée en utilisant une autre projection ; sans quoi l'utilisation de MAP_FIXED est hasardeuse car elle supprime brutalement des projections préexistantes, ce qui facilite la corruption par un processus multithread de son propre espace d'adressage.
Par exemple, supposons qu'un thread A cherche dans /proc/<pid>/maps une plage d'adresses inutilisée où il peut se projeter en utilisant MAP_FIXED, tandis qu'un thread B acquiert en même temps tout ou partie de cette même plage d'adresses. Quand le thread A utilisera ensuite mmap(MAP_FIXED), il va de fait écraser la projection créée par le thread B. Dans ce scénario, le thread B ne doit pas créer de projection directement ; un appel de bibliothèque qui, en interne, utilise dlopen(3) pour charger d'autres bibliothèques partagées, est suffisant. L'appel dlopen(3) projettera la bibliothèque dans l'espace d'adressage du processus. De plus, presque tous les appels de bibliothèques peuvent être implémentés d'une manière qui ajoute des projections de mémoire aux espaces d'adressage, à l'aide de cette technique ou en allouant simplement de la mémoire. Parmi les exemples, figurent brk(2), malloc(3), pthread_create(3) et les bibliothèques PAM http://www.linux-pam.org.
Depuis Linux 4.17, un programme multithreadé peut utiliser l'attribut MAP_FIXED_NOREPLACE pour éviter le risque décrit ci-dessus quand on essaie de créer une projection à une adresse fixe non réservée par une projection préexistante.
Pour les projections prises en charge par un fichier, le champ st_atime du fichier peut être mis à jour à tout moment entre l'appel mmap() et le munmap() correspondant. Le premier accès dans la page projetée mettra le champ à jour si cela n'a pas été déjà fait.
Les champs st_ctime et st_mtime pour un fichier projeté avec PROT_WRITE et MAP_SHARED seront mis à jour après une écriture dans la région projetée, et avant l'éventuel msync(2) suivant avec l'attribut MS_SYNC ou MS_ASYNC.
Pour les projections qui utilisent des pages immenses, les exigences des attributs de mmap() et de munmap() diffèrent quelque peu de celles pour des projections qui utilisent la taille native des pages du système.
Pour mmap(), offset doit être un multiple de la taille de la page immense sous-jacente. Le système aligne automatiquement length pour qu'il soit un multiple de la taille de la page immense sous-jacente.
Pour munmap(), addr et length doivent être tous deux un multiple de la taille de la page immense sous-jacente.
Cette page décrit l'interface fournie par la fonction mmap() de la glibc. Initialement, cette fonction appelait un appel système du même nom. Depuis le noyau 2.4, cet appel système a été remplacé par mmap2(2). De nos jours, la fonction mmap() de la glibc appelle mmap2(2) avec la bonne valeur pour offset.
Sous Linux, il n'y a aucune garantie comme celles indiquées plus haut à propos de MAP_NORESERVE. Par défaut, n'importe quel processus peut être tué à tout moment lorsque le système n'a plus de mémoire.
Dans les noyaux antérieurs à 2.6.7, le drapeau MAP_POPULATE n'avait d'effet que si prot était PROT_NONE.
SUSv3 indique que mmap() devrait échouer si length est 0. Cependant, avec les versions de Linux antérieures à 2.6.12, mmap() réussissait dans ce cas : aucune projection n'était créée, et l'appel renvoyait addr. Depuis le noyau 2.6.12, mmap() échoue avec le code d'erreur EINVAL si length est nul.
POSIX spécifie que le système devrait toujours remplir de zéros toutes les pages incomplètes à la fin de l'objet et que le système n'écrira jamais de modification de l'objet au-delà de sa fin. Sous Linux, lors de l'écriture de données vers ce genre de pages incomplètes après la fin de l'objet, les données restent dans le cache de page même après que le fichier soit fermé et déprojeté, et même si les données ne sont jamais écrites vers le fichier lui-même, les projections suivantes pourraient voir le contenu modifié. Dans certains cas, cela pourrait être corrigé en appelant msync(2) avant la déprojection. Cependant, cela ne fonctionne pas sur tmpfs(5) (par exemple en utilisant l'interface de mémoire partagée POSIX documentée dans shm_overview(7)).
Le programme suivant affiche la partie du fichier, précisé par le premier argument de la ligne de commande, sur la sortie standard. Les octets qui seront affichés sont précisés à partir d'un offset (déplacement) et d'une longueur en deuxième et troisième paramètre. Le code fait une projection mémoire des pages nécessaires du fichier puis utilise write(2) pour afficher les octets voulus.
#include <sys/mman.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0) int main(int argc, char *argv[]) {
char *addr;
int fd;
struct stat sb;
off_t offset, pa_offset;
size_t length;
ssize_t s;
if (argc < 3 || argc > 4) {
fprintf(stderr, "%s fichier offset [longueur]\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if (fd == -1)
handle_error("open");
if (fstat(fd, &sb) == -1) /* Pour obtenir la taille du fichier */
handle_error("fstat");
offset = atoi(argv[2]);
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
/* la position de mmap() doit être alignée sur la page */
if (offset >= sb.st_size) {
fprintf(stderr, "L'offset dépasse la fin du fichier\n");
exit(EXIT_FAILURE);
}
if (argc == 4) {
length = atoi(argv[3]);
if (offset + length > sb.st_size)
length = sb.st_size - offset;
/* Impossible d'afficher les octets en dehors du fichier */
} else { /* Pas de paramètre longueur
==> affichage jusqu'à la fin du fichier */
length = sb.st_size - offset;
}
addr = mmap(NULL, length + offset - pa_offset, PROT_READ,
MAP_PRIVATE, fd, pa_offset);
if (addr == MAP_FAILED)
handle_error("mmap");
s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
if (s != length) {
if (s == -1)
handle_error("write");
fprintf(stderr, "écriture partielle");
exit(EXIT_FAILURE);
}
munmap(addr, length + offset - pa_offset);
close(fd);
exit(EXIT_SUCCESS); }
ftruncate(2), getpagesize(2), memfd_create(2), mincore(2), mlock(2), mmap2(2), mprotect(2), mremap(2), msync(2), remap_file_pages(2), setrlimit(2), shmat(2), userfaultfd(2), shm_open(3), shm_overview(7)
Dans proc(5), les descriptions des fichiers /proc/[pid]/maps, /proc/[pid]/map_files et /proc/[pid]/smaps.
B.O. Gallmeister, POSIX.4, O'Reilly, pp. 128–129 et 389–391.
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/.
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.
21 décembre 2020 | Linux |