mmap, munmap - Établir/supprimer une projection en mémoire
(map/unmap) des fichiers ou des périphériques
Bibliothèque C standard (
libc,
-lc)
#include <sys/mman.h>
void *mmap(void addr[.length], size_t length, int prot, int flags,
int fd, off_t offset);
int munmap(void addr[.length], 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 :
- PROT_EXEC
- Il est possible d'exécuter les pages.
- PROT_READ
- Il est possible de lire les pages.
- PROT_WRITE
- Il est possible d'écrire les pages.
- PROT_NONE
- Il n'est pas possible d'accéder aux pages.
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 la même
région et (en cas de projection file-backed) elles sont
appliquées au fichier sous-jacent (pour contrôler
précisément le moment des mises à jour du fichier
sous-jacent, il faut utiliser msync(2)).
-
MAP_SHARED_VALIDATE (depuis Linux 4.15)
- Cet attribut apporte le même comportement que
MAP_SHARED sauf que les projections MAP_SHARED ignorent les
attributs inconnus dans flags. Au contraire, lors de la
création d'une projection en utilisant MAP_SHARED_VALIDATE,
le noyau vérifie que tous les attributs passés sont connus
et il fait échouer la projection avec l'erreur EOPNOTSUPP
pour les attributs inconnus. Ce type de projection est aussi
nécessaire pour pouvoir utiliser certains attributs de projection
(comme MAP_SYNC).
- 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 dans la
région projetée.
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) :
-
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 fil d'exécution
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 ; fourni pour une
compatibilité avec d'autres implémentations.
- MAP_ANONYMOUS
- La projection n'est prise en charge par aucun
fichier ; son contenu est initialisé à 0.
L'argument fd est ignoré ; 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. L'argument offset
devrait être 0. La prise en charge de MAP_ANONYMOUS
avec MAP_SHARED a été ajoutée dans
Linux 2.4.
- MAP_DENYWRITE
- Cet attribut est ignoré (autrefois
— Linux 2.0 et antérieur — il
signalait une tentative d'écriture dans le fichier sous-jacent et
qui échouait avec l'erreur ETXTBUSY. Mais cela 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 correctement alignée : pour la plupart des
architectures, un multiple de la taille de page suffit, toutefois
certaines architectures imposent des limitations supplémentaires.
Si la zone mémoire indiquée par addr et length
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.
- Les logiciels qui aspirent à la portabilité
devraient utiliser l'attribut MAP_FIXED prudemment, en gardant
à l'esprit que l'aspect exact des projections de mémoire du
processus est autorisé à changer significativement entre les
versions de Linux, de la glibc et du système d'exploitation.
Lisez attentivement le point sur cet attribut dans les
NOTES !
-
MAP_FIXED_NOREPLACE (depuis Linux 4.17)
- Cet attribut offre un comportement identique à
MAP_FIXED par rapport à l'application de addr, mais
il s'en distingue en ce que MAP_FIXED_NOREPLACE
n’écrase jamais une plage projetée existante. Si la
plage demandée entre en conflit avec une projection existante, cet
appel échoue avec l'erreur EEXIST. Cet attribut peut donc
être utilisé comme une façon d'essayer de projeter
une plage d'adresses de manière atomique (vis-à-vis d'autres
fils d'exécutions) : un thread réussira, tous les
autres signaleront un échec.
- Remarquez que les anciens noyaux qui ne reconnaissent pas
l'attribut MAP_FIXED_NOREPLACE se rabattront
généralement (en détectant une collision avec une
projection existante) sur un type de comportement « sans
MAP_FIXED » : ils renverront une adresse
différente de celle demandée. Les logiciels
rétro-compatibles devront donc vérifier l'adresse
renvoyée vis-à-vis de l'adresse demandée.
- MAP_GROWSDOWN
- Cet attribut est utilisé pour les piles. Il indique
au noyau le système de mémoire virtuelle vers lequel la
projection devrait descendre dans la mémoire. L'adresse
renvoyée est une page inférieure à la zone de
mémoire créée dans l'espace d'adressage virtuel du
processus. La modification d'une adresse dans la page de
« garde » sous la projection fera agrandir la
projection d’une page. Cette croissance peut se
répéter jusqu'à ce que la taille de la projection
dans la page atteigne l’extrémité haute de la
projection plus basse suivante, point où la modification de la page
de « garde » donnera un signal
SIGSEGV.
-
MAP_HUGETLB (depuis Linux 2.6.32)
- Allouer la projection à l'aide de
« pages immenses ». Consultez le fichier
Documentation/vm/hugetlbpage.txt des sources du noyau Linux pour
plus d'informations ainsi que les NOTES ci-dessous.
-
MAP_HUGE_2MB, MAP_HUGE_1GB (depuis Linux
3.8)
- Utilisé avec MAP_HUGETLB pour
sélectionner d'autres tailles de pages immenses (hugetlb)
(respectivement 2 Mo et 1 Go) sur les systèmes qui
gèrent plusieurs tailles de page hugetlb.
- Plus généralement, la taille de la page
immense souhaitée peut être configurée en encodant le
logarithme de base 2 de la taille de la page désirée dans
les six bits situés sur MAP_HUGE_SHIFT (une valeur de
zéro dans ce champ de bit fournit la taille de page immense par
défaut ; vous pouvez connaître celle-ci à
l'aide du champ Hugepagesize qui apparaît dans
/proc/meminfo). Ainsi, les deux constantes ci-dessus sont
définies comme suit :
-
#define MAP_HUGE_2MB (21 << MAP_HUGE_SHIFT)
#define MAP_HUGE_1GB (30 << MAP_HUGE_SHIFT)
- Vous pouvez connaître l'intervalle de tailles des
pages immenses gérées par le système en listant les
sous-répertoires de /sys/kernel/mm/hugepages.
-
MAP_LOCKED (depuis Linux 2.5.37)
- Marquer la région projetée pour qu'elle soit
verrouillée de la même manière que mlock(2).
Cette implémentation essaiera de remplir (prefault) toute la plage
mais l'appel mmap() n’échouera pas avec ENOMEM
si cela échoue. Des erreurs énormes pourraient donc se
produire ultérieurement. La sémantique n'est donc pas aussi
robuste que mlock(2). Vous devriez utiliser mmap() et
mlock(2) si d'énormes erreurs ne sont pas acceptables
après l'initialisation de la projection. L'attribut
MAP_LOCKED est ignoré sur les anciens noyaux.
-
MAP_NONBLOCK (depuis Linux 2.5.46)
- Cet attribut 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). Avant
Linux 2.6, cet attribut n'avait d'effet que pour les projections
privées modifiables.
-
MAP_POPULATE (depuis Linux 2.5.46)
- Remplir (prefault) les tables de pages pour une projection.
Pour une projection de fichier, cela provoque une lecture anticipée
du fichier. Les accès ultérieurs à la projection ne
seront pas bloqués par des erreurs de pages. L'appel mmap
n'échoue pas si la projection ne peut pas être remplie (par
exemple à cause de limitation sur le nombre de pages immenses
mappées lors de l'utilisation de MAP_HUGETLB). La prise en
charge de MAP_POPULATE avec les projections privées a
été ajoutée dans Linux 2.6.23.
-
MAP_STACK (depuis Linux 2.6.27)
- Allouer la projection à une adresse qui convient
à la pile d'un processus ou d'un thread.
- Cet attribut n'est pas opérationnel pour l'instant
sur Linux. Mais son utilisation permet aux applications de s'assurer
qu'elles le gèreront de manière transparente s'il est
implémenté dans le futur. Ainsi, il est utilisé dans
l'implémentation de threading de la glibc pour accepter le fait que
certaines architectures pourront (plus tard) nécessiter un
traitement spécial pour allouer des piles. Une autre raison
d'utiliser cet attribut est la portabilité :
MAP_STACK existe et a un effet sur d'autres systèmes (comme
certains BSD).
-
MAP_SYNC (depuis Linux 4.15)
- Cet attribut n'est possible qu'avec le type de projection
MAP_SHARED_VALIDATE ; les projections de type
MAP_SHARED ignoreront silencieusement cet attribut. Il n'est pris
en charge que pour des fichiers gérant le DAX (projection directe
de mémoire persistante). Pour les autres fichiers, créer une
projection avec cet attribut provoquera une erreur EOPNOTSUPP.
- Les projections de fichier partagé ayant cet
attribut fournissent une garantie que tant que la mémoire est
projetée avec accès en écriture dans l'espace
d'adressage du processus, elle sera visible dans le même fichier et
au même endroit même après un plantage ou un
redémarrage du système. Couplé à l'utilisation
des instructions adéquates du processeur, cela offre aux
utilisateurs de telles projections une manière plus efficace de
rendre des modifications de données persistantes.
-
MAP_UNINITIALIZED (depuis Linux 2.6.33)
- Ne pas effacer 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 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 défini pour indiquer l'erreur.
S'il réussit,
munmap() renvoie
0. En cas d'échec,
-1 est renvoyé et
errno est défini pour indiquer
l'erreur (probablement
EINVAL).
- EACCES
- Le descripteur ne correspond pas à un fichier normal
ou une demande de projection de fichier a été
demandée mais fd n'est pas ouvert en lecture, ou une demande
de projection partagée MAP_SHARED avec protection
PROT_WRITE a été demandée 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é).
- EEXIST
-
MAP_FIXED_NOREPLACE était indiqué dans
flags et la plage couverte par addr et length est en
conflit avec une projection existante.
- EINVAL
-
addr ou length ou offset sont non
valables (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 contenait ni MAP_PRIVATE, ni
MAP_SHARED, ni MAP_SHARED_VALIDATE.
- ENFILE
- La limite du nombre total de fichiers ouverts pour le
système entier a été atteinte.
- ENODEV
- Le système de fichiers sous‐jacent ne
gère pas la projection en mémoire.
- ENOMEM
- Aucune mémoire disponible.
- ENOMEM
- Le nombre maximal de projections du processus serait
dépassé. Cette erreur peut aussi se produire pour
munmap() lors de la suppression d'une projection d'une
région au milieu d'une projection existante, puisque cela provoque
deux régions plus petites de chaque côté de la
région à supprimer.
- ENOMEM
- (Depuis Linux 4.7) La limite RLIMIT_DATA du
processus, décrite dans getrlimit(2), serait
dépassée.
- ENOMEM
-
addr n'est pas apprécié parce qu'il
dépasse l'espace d'adressage virtuel du processeur
- 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).
- 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.
- EPERM
- La lecture a été interrompue par un
signal ; consultez fnctl(2).
- EPERM
- L'attribut MAP_HUGETLB était indiqué,
mais l'appelant n'était pas priviliégié (il n'avait
pas la capacité CAP_IPC_LOCK) et n'appartient pas au groupe
sysctl_hugetlb_shm_group ; voir la desription de
/proc/sys/vm/sysctl_hugetlb_shm_group dans
- ETXTBSY
-
MAP_DENYWRITE a été
réclamé mais fd est ouvert en écriture.
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 page du tampon
au-delà de la fin du fichier projeté. Pour une explication
du traitement des octets dans la page correspondant à la fin du
fichier projeté n'étant pas un multiple de la taille de la
page, voir les NOTES.
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 Linux 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.
Avant Linux 2.6.7, l'attribut
MAP_POPULATE n'avait d'effet que si
prot était
PROT_NONE.
SUSv3 indique que
mmap() devrait échouer si
length est 0.
Cependant, avant Linux 2.6.12,
mmap() réussissait dans ce
cas : aucune projection n'était créée, et l'appel
renvoyait
addr. Depuis Linux 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 <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
int
main(int argc, char *argv[])
{
int fd;
char *addr;
off_t offset, pa_offset;
size_t length;
ssize_t s;
struct stat sb;
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, p. 128–129 et 389–391.
La traduction française de cette page de manuel a été
créée par Christophe Blaess
<
https://www.blaess.fr/christophe/>, Stéphan Rafin
<
[email protected]>, Thierry Vignaud
<
[email protected]>, François Micaux, Alain Portal
<
[email protected]>, Jean-Philippe Guérard
<
[email protected]>, Jean-Luc Coulon (f5ibh)
<
[email protected]>, Julien Cristau
<
[email protected]>, Thomas Huriaux <
[email protected]>,
Nicolas François <
[email protected]>, Florentin
Duneau <
[email protected]>, Simon Paillard
<
[email protected]>, Denis Barbier
<
[email protected]>, David Prévot <
[email protected]> et
Jean-Philippe MENGUAL <
[email protected]>
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 à
[email protected]