mmap(2) Établir/supprimer une projection en mémoire (map/unmap) des

Other Alias

munmap

SYNOPSIS

#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é.

DESCRIPTION

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.

Si addr est NULL, le noyau choisit l'adresse à 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. 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 page, renvoyée par sysconf(_SC_PAGE_SIZE).

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 :

PROT_EXEC
On peut exécuter du code dans la zone mémoire.
PROT_READ
On peut lire le contenu de la zone mémoire.
PROT_WRITE
On peut écrire dans la zone mémoire.
PROT_NONE
Les pages ne peuvent pas être accédées.

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
Partager la projection. Les modifications de la projection sont visibles dans les autres processus qui projettent ce fichier, et sont appliquées au fichier sous-jacent. En revanche, ce dernier n'est pas nécessairement mis à jour tant qu'on n'a pas appelé msync(2) ou munmap().
MAP_PRIVATE
Créer une projection privée, utilisant la méthode de copie à l'écriture. Les modifications de la projection ne sont pas visibles depuis les autres processus projetant le même fichier, et ne modifient pas le fichier lui-même. Il n'est pas précisé si les changements effectués dans le fichier après l'appel mmap() seront visibles.

Ces deux attributs sont décrits dans POSIX.1-2001.

De plus, zéro ou plus des valeurs suivantes peuvent être incluses dans flags (avec un OU binaire) :

MAP_32BIT (depuis Linux 2.4.20, 2.6)
Placer la projection dans les deux premiers gigaoctets de l'espace d'adressage du processus. Cet attribut n'est pris en charge que sous x86-64, pour les programmes 64 bits. Il a été ajouté pour permettre à la pile d'un thread d'être allouée dans les deux premiers gigaoctets de mémoire, afin d'améliorer les performances des changements de contexte sur les premiers processeurs 64 bits. Les processeurs x86-64 modernes n'ont plus ces problèmes de performance, donc l'utilisation de cet attribut n'est pas nécessaire sur ces systèmes. L'attribut MAP_32BIT est ignoré quand MAP_FIXED est positionné.
MAP_ANON
Synonyme de MAP_ANONYMOUS. Déconseillé.
MAP_ANONYMOUS
La projection n'est supportée par aucun fichier ; son contenu est initialisé à 0. Les arguments fd et offset sont ignorés ; cependant, certaines implémentations demandent que fd soit -1 si MAP_ANONYMOUS (ou MAP_ANON) est utilisé, et les applications portables doivent donc s'en assurer. Cet attribut, utilisé en conjonction de MAP_SHARED, n'est implémenté que depuis Linux 2.4.
MAP_DENYWRITE
Cet attribut est ignoré. (Autrefois, une tentative d'écriture dans le fichier sous-jacent échouait avec l'erreur ETXTBUSY. Mais ceci permettait des attaques par déni de service.)
MAP_EXECUTABLE
Cet attribut est ignoré.
MAP_FILE
Attribut pour compatibilité. Ignoré.
MAP_FIXED
Ne pas considérer addr comme une indication : n'utiliser que l'adresse indiquée. addr doit être un multiple de la taille de page. Si la zone mémoire indiquée par addr et len recouvre des pages d'une projection existante, la partie recouverte de la projection existante sera ignorée. Si l'adresse indiquée ne peut être utilisée, mmap() échouera. Il est déconseillé d'utiliser cette option, car requérir une adresse fixe pour une projection n'est pas portable.
MAP_GROWSDOWN
Utilisé pour les piles. Indique au système de gestion de la mémoire virtuelle que la projection doit s'étendre en croissant vers le bas de la mémoire.
MAP_HUGETLB (depuis Linux 2.6.32)
Allouer la projection à l'aide de « pages immenses ». Consultez le fichier source du noyau Linux Documentation/vm/hugetlbpage.txt pour plus d'informations.
MAP_LOCKED (depuis Linux 2.5.37)
Verrouille la page projetée en mémoire à la manière de mlock(2). Cet attribut est ignoré sur les noyaux plus anciens.
MAP_NONBLOCK (depuis Linux 2.5.46)
N'a de sens qu'en conjonction avec MAP_POPULATE. Ne pas effectuer de lecture anticipée : créer seulement les entrées de tables de page pour les pages déjà présentes en RAM. Depuis Linux 2.6.23, cet attribut fait que MAP_POPULATE n'a aucun effet. Un jour la combinaison de MAP_POPULATE et MAP_NONBLOCK pourra être implémentée de nouveau.
MAP_NORESERVE
Ne pas réserver d'espace de swap pour les pages de cette projection. Une telle réservation garantit que l'on puisse modifier les zones soumises à une copie-en-écriture. Sans réservation, on peut recevoir un signal SIGSEGV durant une écriture, s'il n'y a plus de place disponible. Consultez également la description du fichier /proc/sys/vm/overcommit_memory dans la page proc(5). Dans les noyaux antérieurs à 2.6, cet attribut n'avait d'effet que pour les projections privées modifiables.
MAP_POPULATE (depuis Linux 2.5.46)
Remplit les tables de pages pour une projection. Pour une projection de fichier, ceci s'effectue par une lecture anticipée du fichier. Les accès ultérieurs à la projection ne seront pas bloqués par des fautes de pages. MAP_POPULATE n'est géré pour les projections privées que depuis Linux 2.6.23.
MAP_STACK (depuis Linux 2.6.27)
Alloue la projection à une adresse qui convient pour la pile d'un processus ou d'un thread. Cet attribut n'a pour l'instant aucun effet, mais est utilisé par l'implémentation des threads de la glibc de telle sorte que si certaines architectures nécessitent un traitement particulier pour l'allocation de la pile, leur prise en charge par la suite par la glibc pourra être implémentée de façon transparente.
MAP_UNINITIALIZED (depuis Linux 2.6.33)
N'efface pas les pages anonymes. Cet attribut n'a pour l'instant un effet que si le noyau a été configuré avec l'option CONFIG_MMAP_ALLOW_UNINITIALIZED. À cause des implications sur la sécurité, cette option n'est normalement active que sur des périphériques embarqués (c'est-à-dire avec des périphériques avec lesquels il est possible d'avoir un contrôle total de la mémoire utilisateur).

Parmi les attributs ci-dessus, seul MAP_FIXED est spécifié dans POSIX.1-2001. Cependant, la plupart des systèmes gèrent aussi MAP_ANONYMOUS (ou son synonyme MAP_ANON).

Certains systèmes utilisent les attributs supplémentaires MAP_AUTOGROW, MAP_AUTORESRV, MAP_COPY et MAP_LOCAL.

La mémoire obtenue par mmap est préservée au travers d'un fork(2), avec les mêmes attributs.

La projection doit avoir une taille multiple de celle des pages. 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.

munmap()

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

Modifications d'horodatage pour les projections supportées par un fichier

Pour les projections supportées 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 attribut MS_SYNC ou MS_ASYNC.

VALEUR RENVOYÉE

mmap() renvoie un pointeur sur la zone de mémoire, s'il réussit. En cas d'échec il retourne la valeur MAP_FAILED (c.-à-d. (void *) -1) et errno contient le code d'erreur. munmap() renvoie 0 s'il réussit. En cas d'échec, -1 est renvoyé et errno contient le code d'erreur (probablement EINVAL).

ERREURS

EACCES
Le descripteur ne correspond pas à un fichier normal, ou on demande une projection de fichier mais fd n'est pas ouvert en lecture, ou on demande une projection partagée MAP_SHARED avec protection PROT_WRITE, mais fd n'est pas ouvert en lecture et écriture (O_RDWR). Ou encore PROT_WRITE est demandé, mais le fichier est ouvert en ajout seulement.
EAGAIN
Le fichier est verrouillé, ou trop de pages ont été verrouillées en mémoire (consultez setrlimit(2)).
EBADF
fd n'est pas un descripteur de fichier valable (et MAP_ANONYMOUS n'était pas précisé).
EINVAL
addr ou length ou offset sont invalides (par exemple : zone trop grande, ou non alignée sur une frontière de page).
EINVAL
(depuis Linux 2.6.12) length est nul.
EINVAL
flags ne contient ni MAP_PRIVATE ni MAP_SHARED, ou les contient tous les deux.
ENFILE
La limite du nombre total de fichiers ouverts sur le système a été atteinte.
ENODEV
Le système de fichiers sous-jacent ne supporte pas la projection en mémoire.
ENOMEM
Pas assez de mémoire, ou le nombre maximal de projections par processus a été dépassé.
EPERM
L'argument prot a demandé PROT_EXEC mais la zone appartient à un fichier sur un système de fichiers monté sans permission d'exécution.
ETXTBSY
MAP_DENYWRITE a été réclamé mais fd est ouvert en écriture.
EOVERFLOW
Sur architecture 32 bits avec l'extension de fichiers très grands (c'est-à-dire utilisant un off_t sur 64 bits) : le nombre de pages utilisées pour length plus le nombre de pages utilisées pour offset dépasserait unsigned long (32 bits).

L'accès à une zone de projection peut déclencher les signaux suivants :

SIGSEGV
Tentative d'écriture dans une zone en lecture seule.
SIGBUS
Tentative d'accès à une portion de la zone qui ne correspond pas au fichier (par exemple après la fin du fichier, y compris lorsqu'un autre processus l'a tronqué).

CONFORMITÉ

SVr4, BSD 4.4, POSIX.1-2001.

DISPONIBILITÉ

Sur les systèmes POSIX sur lesquels mmap(), msync(2) et munmap() sont disponibles, la constante symbolique _POSIX_MAPPED_FILES est définie dans <unistd.h> comme étant une valeur supérieure à 0. (Consultez aussi sysconf(3).)

NOTES

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.

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 _BSD_SOURCE ou _SVID_SOURCE est défini. (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.

BOGUES

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 (par exemple en utilisant l'interface de mémoire partagée POSIX documentée dans shm_overview(7)).

EXEMPLE

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.

Source du programme

#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);
        /* l'offset pour mmap() doit être aligné sur une 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);
    }
    exit(EXIT_SUCCESS);
}

COLOPHON

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

TRADUCTION

Depuis 2010, cette traduction est maintenue à l'aide de l'outil po4a <http://po4a.alioth.debian.org/> par l'équipe de traduction francophone au sein du projet perkamon <http://perkamon.alioth.debian.org/>.

Thierry Vignaud (2002), Alain Portal <http://manpagesfr.free.fr/> (2006). Julien Cristau et l'équipe francophone de traduction de Debian (2006-2009).

Veuillez signaler toute erreur de traduction en écrivant à <[email protected]> ou par un rapport de bogue sur le paquet manpages-fr.

Vous pouvez toujours avoir accès à la version anglaise de ce document en utilisant la commande « man -L C <section> <page_de_man> ».