Memory Mappings sous Linux : mmap(), munmap(), msync(), mremap() et mappings anonymes

Ce forum est dédié à apprendre le développement de programmes user mode sur Linux

Moderator: Rick

Post Reply
Hydraxx
Site Admin
Posts: 114
Joined: Mon Jan 12, 2026 4:04 pm
Location: France
Contact:

Memory Mappings sous Linux : mmap(), munmap(), msync(), mremap() et mappings anonymes

Post by Hydraxx »

Memory Mappings sous Linux :

Code: Select all

mmap()
,

Code: Select all

munmap()
,

Code: Select all

msync()
,

Code: Select all

mremap()
et mappings anonymes[/size][/color]

1. Introduction

Le mécanisme de memory mapping permet d'associer une région de l'espace d'adressage virtuel d'un processus à une ressource.

Cette ressource peut être :
  • une portion de fichier ;
  • une zone mémoire anonyme qui n'est associée à aucun fichier ;
  • une région partagée entre plusieurs processus ;
  • une zone servant à réaliser des entrées/sorties mémoire-mappées.
Sous Linux et UNIX, l'appel système central est

Code: Select all

mmap()
.

Un mapping mémoire permet donc au programme de manipuler directement des octets via des adresses mémoire, sans nécessairement employer

Code: Select all

read()
et

Code: Select all

write()
pour chaque accès.

Exemple mental :

Code: Select all

Fichier sur disque
        |
        v
Noyau / cache de pages
        |
        v
Espace virtuel du processus
        |
        v
pointeur retourné par mmap()
Une fois la région mappée, le programme peut lire ou modifier les données via un pointeur.

Les mappings sont utilisés pour :
  • charger efficacement des fichiers ;
  • partager de la mémoire entre processus ;
  • implémenter des allocateurs ;
  • charger des bibliothèques partagées ;
  • charger les segments d'un exécutable ;
  • faire du memory-mapped I/O ;
  • partager une zone mémoire entre parent et enfant après

    Code: Select all

    fork()
    ;
  • réduire le nombre d'appels

    Code: Select all

    read()/write()
    .

2. Deux grandes familles de mappings

Il existe deux grandes catégories.

2.1 Mapping de fichier

Un mapping de fichier associe une région d'un fichier à une région de mémoire virtuelle.

Le noyau charge les pages nécessaires à la demande.

Le programme peut ensuite accéder au fichier avec de simples lectures/écritures mémoire.

Exemple :

Code: Select all

char *p = mmap(...);

printf("%c\n", p[0]);
Ici,

Code: Select all

p[0]
correspond à un octet du fichier mappé.

2.2 Mapping anonyme

Un mapping anonyme n'est associé à aucun fichier.

Les pages sont initialement remplies de zéros.

Il peut servir comme une sorte de mémoire virtuelle allouée directement auprès du noyau.

Exemple typique :

Code: Select all

void *p = mmap(NULL,
               4096,
               PROT_READ | PROT_WRITE,
               MAP_PRIVATE | MAP_ANONYMOUS,
               -1,
               0);
C'est conceptuellement proche d'une allocation de mémoire virtuelle.


3. Mapping privé et mapping partagé

Indépendamment du fait qu'un mapping soit basé sur un fichier ou anonyme, il peut être privé ou partagé.

3.1

Code: Select all

MAP_PRIVATE
[/color]

Code: Select all

MAP_PRIVATE
crée un mapping privé.

Les modifications réalisées par le processus ne sont pas propagées vers le fichier sous-jacent.

Le système utilise typiquement le mécanisme Copy-On-Write.

Concept :

Code: Select all

Processus A
   |
   +---- page partagée initialement
   |
   +---- modification
             |
             v
        copie privée de la page
Tant que la page n'est pas modifiée, elle peut être partagée.

Lors d'une écriture, le noyau crée une copie privée pour le processus.

Conséquences :
  • les changements ne sont pas visibles dans le fichier ;
  • les changements ne sont pas nécessairement visibles par d'autres processus ;
  • ce mode est utilisé notamment pour charger des exécutables et bibliothèques.
3.2

Code: Select all

MAP_SHARED
[/color]

Code: Select all

MAP_SHARED
crée un mapping partagé.

Les modifications effectuées sur le mapping sont visibles par les autres processus qui mappent la même région en mode partagé.

Dans le cas d'un fichier, les changements peuvent également être propagés vers le fichier.

Exemple :

Code: Select all

Processus A ----\
                 +---- mêmes pages physiques ---- fichier
Processus B ----/
C'est donc un mécanisme d'IPC très efficace.

Attention :

Code: Select all

MAP_SHARED
fournit une mémoire commune, mais ne fournit pas automatiquement la synchronisation.

Si plusieurs processus écrivent simultanément, il faut utiliser par exemple :
  • sémaphores ;
  • mutex inter-processus ;
  • futex ;
  • verrous de fichiers ;
  • atomiques appropriés.

4. L'appel système

Code: Select all

mmap()
[/size][/color]

Prototype :

Code: Select all

#include <sys/mman.h>

void *mmap(void *addr,
           size_t length,
           int prot,
           int flags,
           int fd,
           off_t offset);
Valeur de retour :
  • succès : adresse de début du mapping ;
  • échec :

    Code: Select all

    MAP_FAILED
    .
Toujours tester :

Code: Select all

void *p = mmap(...);

if (p == MAP_FAILED) {
    perror("mmap");
}
Ne pas tester contre

Code: Select all

NULL
.

Code: Select all

MAP_FAILED
est généralement défini comme :

Code: Select all

(void *) -1

5. Paramètre

Code: Select all

addr
[/size][/color]

Code: Select all

addr
indique l'adresse souhaitée pour le mapping.

Dans la majorité des cas :

Code: Select all

addr = NULL
On laisse alors le noyau choisir une adresse virtuelle appropriée.

Exemple :

Code: Select all

void *p = mmap(NULL, 4096, ...);
Le noyau choisit automatiquement une zone libre.

Si

Code: Select all

addr
est non

Code: Select all

NULL
, il peut servir d'indication d'adresse.

Sans

Code: Select all

MAP_FIXED
, le noyau n'est généralement pas obligé d'utiliser exactement cette adresse.

Bonne pratique :
  • utiliser

    Code: Select all

    NULL
    sauf besoin très spécifique ;
  • éviter de choisir soi-même des adresses fixes.

6. Paramètre

Code: Select all

length
[/size][/color]

Code: Select all

length
indique la taille du mapping en octets.

Exemple :

Code: Select all

size_t length = 4096;

mmap(NULL, length, ...);
Le mapping est réalisé au niveau des pages mémoire.

La taille de page peut être obtenue par :

Code: Select all

long page_size = sysconf(_SC_PAGESIZE);
ou :

Code: Select all

getpagesize();
Sur de nombreux systèmes x86-64 :

Code: Select all

page_size = 4096
mais il ne faut pas coder cette valeur en dur si le programme doit être portable.


7. Paramètre

Code: Select all

prot
: protections mémoire[/size][/color]

Code: Select all

prot
indique les opérations autorisées sur les pages.

Les valeurs principales sont :
  • Code: Select all

    PROT_NONE
    : aucun accès autorisé ;
  • Code: Select all

    PROT_READ
    : lecture autorisée ;
  • Code: Select all

    PROT_WRITE
    : écriture autorisée ;
  • Code: Select all

    PROT_EXEC
    : exécution autorisée.
Les

Code: Select all

flags
peuvent être combinés avec OR binaire.

Exemple :

Code: Select all

PROT_READ | PROT_WRITE
Cela crée une région lisible et modifiable.

Exemple lecture seule :

Code: Select all

PROT_READ
Exemple lecture + exécution :

Code: Select all

PROT_READ | PROT_EXEC
Exemple inaccessible :

Code: Select all

PROT_NONE

Code: Select all

PROT_NONE
peut être utilisé pour créer des guard pages.

Une tentative d'accès à une page protégée peut provoquer

Code: Select all

SIGSEGV
.

Important :
les permissions demandées doivent être compatibles avec la manière dont le fichier a été ouvert.

Par exemple, un mapping partagé modifiable nécessite normalement un descripteur ouvert avec des droits d'écriture adaptés.


8. Paramètre

Code: Select all

flags
[/size][/color]

Code: Select all

flags
contrôle le type et le comportement du mapping.

Il faut normalement choisir au moins un des deux :
D'autres

Code: Select all

flags
peuvent être combinés.

Exemple :

Code: Select all

MAP_PRIVATE | MAP_ANONYMOUS

9. Paramètre

Code: Select all

fd
[/size][/color]

Pour un mapping de fichier,

Code: Select all

fd
est le descripteur obtenu avec

Code: Select all

open()
.

Exemple :

Code: Select all

int fd = open("data.bin", O_RDONLY);

void *p = mmap(NULL,
               length,
               PROT_READ,
               MAP_PRIVATE,
               fd,
               0);
Une fois

Code: Select all

mmap()
réussi, le descripteur peut généralement être fermé.

Exemple :

Code: Select all

close(fd);
Le mapping reste valide.

La durée de vie du mapping n'est donc pas liée à celle du descripteur de fichier.


10. Paramètre

Code: Select all

offset
[/size][/color]

Code: Select all

offset
indique à quel endroit du fichier commence le mapping.

Exemple :

Code: Select all

offset = 0
mappe à partir du début du fichier.

L'

Code: Select all

offset
doit respecter les contraintes d'alignement du système.

En pratique, il doit être un multiple de la taille de page.

Exemple :

Code: Select all

long ps = sysconf(_SC_PAGESIZE);

off_t offset = 2 * ps;

11. Exemple simple : mapper un fichier en lecture

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>

int main(void)
{
    int fd = open("test.txt", O_RDONLY);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    struct stat st;

    if (fstat(fd, &st) == -1) {
        perror("fstat");
        close(fd);
        return EXIT_FAILURE;
    }

    if (st.st_size == 0) {
        printf("Fichier vide\n");
        close(fd);
        return EXIT_SUCCESS;
    }

    char *addr = mmap(NULL,
                      st.st_size,
                      PROT_READ,
                      MAP_PRIVATE,
                      fd,
                      0);

    if (addr == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return EXIT_FAILURE;
    }

    close(fd);

    write(STDOUT_FILENO, addr, st.st_size);

    if (munmap(addr, st.st_size) == -1) {
        perror("munmap");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}
Étapes :
12.

Code: Select all

munmap()
: supprimer un mapping[/size][/color]

Prototype :

Code: Select all

#include <sys/mman.h>

int munmap(void *addr, size_t length);
Valeur de retour : Exemple :

Code: Select all

if (munmap(addr, length) == -1) {
    perror("munmap");
}
Après

Code: Select all

munmap()
, les adresses de cette région ne sont plus valides.

Accéder à la zone après

Code: Select all

munmap()
est une erreur et peut provoquer

Code: Select all

SIGSEGV
.

Il est possible de supprimer :
  • tout le mapping ;
  • une partie du mapping ;
  • plusieurs pages contiguës.
L'adresse doit respecter les contraintes d'alignement appropriées.


13. Mapping de fichier : procédure générale

La procédure classique est :
  • 1. ouvrir le fichier avec

    Code: Select all

    open()
    ;
  • 2. récupérer éventuellement sa taille avec

    Code: Select all

    fstat()
    ;
  • 3. appeler

    Code: Select all

    mmap()
    ;
  • 4. utiliser la région comme une zone mémoire ;
  • 5. éventuellement appeler

    Code: Select all

    msync()
    ;
  • 6. appeler

    Code: Select all

    munmap()
    ;
  • 7. fermer le

    Code: Select all

    fd
    s'il ne l'a pas déjà été.

14. Memory-mapped I/O

Avec un fichier mappé, les données peuvent être manipulées comme de la mémoire.

Au lieu de :

Code: Select all

read(fd, buffer, size);
on peut faire :

Code: Select all

char *addr = mmap(...);

printf("%c\n", addr[100]);
Pour modifier :

Code: Select all

addr[100] = 'A';
Le noyau gère :
  • le chargement des pages ;
  • les page faults ;
  • le cache de pages ;
  • la propagation éventuelle vers le fichier.
Avantages :
  • interface simple pour accès aléatoires ;
  • moins de copies explicites ;
  • peut éviter certains appels

    Code: Select all

    read()/write()
    ;
  • partage naturel des pages du cache ;
  • très pratique pour les gros fichiers structurés.
Inconvénients :
  • gestion des limites de fichier ;
  • Code: Select all

    SIGBUS
    possible lors de certains accès invalides ;
  • page faults moins visibles ;
  • synchronisation plus délicate ;
  • pas toujours plus performant pour de petits accès séquentiels ;
  • plus difficile à maîtriser précisément que

    Code: Select all

    read()/write()
    .

15. Pourquoi

Code: Select all

mmap()
peut être performant[/size][/color]

Avec

Code: Select all

read()
, le chemin conceptuel peut être :

Code: Select all

fichier
   |
   v
cache noyau
   |
   v
buffer utilisateur
Avec

Code: Select all

mmap()
:

Code: Select all

fichier
   |
   v
cache noyau / pages
   |
   v
pages directement visibles dans l'espace virtuel
Cela peut éviter certaines copies explicites entre espace noyau et espace utilisateur.

Mais

Code: Select all

mmap()
n'est pas automatiquement plus rapide.

Le gain dépend :
  • de la taille des données ;
  • du type d'accès ;
  • du nombre d'accès aléatoires ;
  • de la pression mémoire ;
  • du comportement du cache ;
  • du coût des page faults.

16.

Code: Select all

MAP_SHARED
et fichiers[/size][/color]

Exemple :

Code: Select all

char *addr = mmap(NULL,
                  size,
                  PROT_READ | PROT_WRITE,
                  MAP_SHARED,
                  fd,
                  0);

addr[0] = 'X';
La modification peut être propagée vers le fichier.

Les autres processus mappant la même zone en

Code: Select all

MAP_SHARED
peuvent également voir le changement.

Important :
la visibilité mémoire et l'écriture physique sur disque sont deux choses différentes.

Le noyau peut garder les pages modifiées dans le cache avant de les écrire réellement sur le stockage.


17.

Code: Select all

msync()
: synchroniser un mapping[/size][/color]

Prototype :

Code: Select all

#include <sys/mman.h>

int msync(void *addr, size_t length, int flags);
Valeur de retour :

Code: Select all

msync()
permet de demander la synchronisation d'un mapping avec le fichier sous-jacent.

Flags principaux :

Code: Select all

MS_SYNC
[/color]

Demande une synchronisation synchrone.

L'appel attend que les données concernées soient écrites conformément au comportement du système.

Exemple :

Code: Select all

msync(addr, length, MS_SYNC);

Code: Select all

MS_ASYNC
[/color]

Demande une synchronisation asynchrone.

L'appel peut retourner sans attendre la fin physique de l'écriture.

Code: Select all

MS_INVALIDATE
[/color]

Demande d'invalider les copies mises en cache lorsque cela est applicable, afin que les vues soient cohérentes avec les données synchronisées.

Exemple :

Code: Select all

if (msync(addr, length, MS_SYNC) == -1) {
    perror("msync");
}

18.

Code: Select all

MAP_PRIVATE
et Copy-On-Write[/size][/color]

Exemple :

Code: Select all

char *addr = mmap(NULL,
                  size,
                  PROT_READ | PROT_WRITE,
                  MAP_PRIVATE,
                  fd,
                  0);

addr[0] = 'X';
La modification touche la vue privée du processus, pas directement le fichier.

Concept :

Code: Select all

avant écriture :

Processus
   |
   v
page fichier

après écriture :

Processus
   |
   v
copie privée modifiée

Fichier
   |
   v
page originale
C'est le mécanisme Copy-On-Write.


19. Mapping anonyme avec

Code: Select all

MAP_ANONYMOUS
[/size][/color]

Code: Select all

MAP_ANONYMOUS
permet de créer une zone mémoire sans fichier.

Exemple :

Code: Select all

void *addr = mmap(NULL,
                  4096,
                  PROT_READ | PROT_WRITE,
                  MAP_PRIVATE | MAP_ANONYMOUS,
                  -1,
                  0);

if (addr == MAP_FAILED) {
    perror("mmap");
}
Les pages sont initialement remplies de zéros.

Pour les mappings anonymes :

Code: Select all

MAP_ANON
est parfois disponible comme alias de

Code: Select all

MAP_ANONYMOUS
.


20.

Code: Select all

/dev/zero
[/size][/color]

Historiquement, certaines applications créaient des mappings anonymes en ouvrant

Code: Select all

/dev/zero.
Exemple conceptuel :

Code: Select all

int fd = open("/dev/zero", O_RDWR);

void *p = mmap(NULL,
               size,
               PROT_READ | PROT_WRITE,
               MAP_PRIVATE,
               fd,
               0);
Aujourd'hui,

Code: Select all

MAP_ANONYMOUS
est normalement préférable sur Linux.


21. Mapping anonyme privé

Code: Select all

MAP_PRIVATE | MAP_ANONYMOUS
est utile comme primitive d'allocation.

Exemple :

Code: Select all

size_t size = 1024 * 1024;

void *p = mmap(NULL,
               size,
               PROT_READ | PROT_WRITE,
               MAP_PRIVATE | MAP_ANONYMOUS,
               -1,
               0);
Cette zone :
  • n'est pas associée à un fichier ;
  • est initialisée à zéro ;
  • est privée au processus ;
  • peut être libérée avec

    Code: Select all

    munmap()
    .
Des allocateurs comme

Code: Select all

malloc()
peuvent utiliser

Code: Select all

mmap()
en interne pour de grosses allocations, selon l'implémentation.


22. Mapping anonyme partagé

Un mapping anonyme peut être partagé.

Exemple :

Code: Select all

int *value = mmap(NULL,
                  sizeof(int),
                  PROT_READ | PROT_WRITE,
                  MAP_SHARED | MAP_ANONYMOUS,
                  -1,
                  0);
Cela est particulièrement utile avec

Code: Select all

fork()
.

Après

Code: Select all

fork()
, le parent et l'enfant peuvent accéder aux mêmes pages physiques si le mapping est

Code: Select all

MAP_SHARED
.


23. Exemple IPC parent/enfant avec

Code: Select all

mmap()
[/size][/color]

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>

int main(void)
{
    int *value = mmap(NULL,
                      sizeof(int),
                      PROT_READ | PROT_WRITE,
                      MAP_SHARED | MAP_ANONYMOUS,
                      -1,
                      0);

    if (value == MAP_FAILED) {
        perror("mmap");
        return EXIT_FAILURE;
    }

    *value = 1;

    pid_t pid = fork();

    if (pid == -1) {
        perror("fork");
        munmap(value, sizeof(int));
        return EXIT_FAILURE;
    }

    if (pid == 0) {
        (*value)++;
        printf("Enfant : %d\n", *value);

        munmap(value, sizeof(int));
        _exit(EXIT_SUCCESS);
    }

    wait(NULL);

    printf("Parent : %d\n", *value);

    munmap(value, sizeof(int));

    return EXIT_SUCCESS;
}
Résultat logique :

Code: Select all

valeur initiale = 1
enfant incrémente => 2
parent voit => 2
Si le mapping était

Code: Select all

MAP_PRIVATE
, le parent ne verrait pas nécessairement l'écriture de l'enfant.


24.

Code: Select all

mmap()
et

Code: Select all

fork()
[/size][/color]

Lors d'un

Code: Select all

fork()
, les mappings du processus parent sont hérités par l'enfant.

Le comportement dépend du type :

Code: Select all

MAP_PRIVATE
:
  • les pages utilisent Copy-On-Write ;
  • les modifications privées ne sont pas partagées.

Code: Select all

MAP_SHARED
:
  • les processus peuvent continuer à partager les pages ;
  • les modifications peuvent être visibles entre eux.
Cela fait de

Code: Select all

mmap()
un mécanisme IPC très puissant.


25. Durée de vie d'un mapping

Un mapping reste normalement présent :
  • jusqu'à

    Code: Select all

    munmap()
    ;
  • jusqu'à la fin du processus ;
  • ou jusqu'à ce qu'il soit remplacé/modifié par une opération appropriée.
Un

Code: Select all

exec()
remplace l'espace d'adressage du processus.

Les mappings précédents ne continuent donc normalement pas après

Code: Select all

exec()
.


26. Alignement et taille de page

Les mappings sont gérés par pages.

Taille de page :

Code: Select all

long ps = sysconf(_SC_PAGESIZE);
L'

Code: Select all

offset
d'un mapping de fichier doit respecter l'alignement exigé par le système.

Si on souhaite mapper un octet situé au milieu d'une page, on mappe généralement à partir de la page précédente puis on ajoute un décalage dans le pointeur.

Exemple conceptuel :

Code: Select all

wanted_offset = 5000
page_size = 4096

aligned_offset = 4096
delta = 5000 - 4096 = 904
On mappe depuis 4096 puis on utilise :

Code: Select all

char *wanted = mapped + 904;

27. Cas limite : taille non multiple de la page

Supposons une page de 4096 octets.

On demande :

Code: Select all

mmap(..., 6000, ...);
Le noyau doit gérer un nombre entier de pages.

La dernière page du mapping peut donc contenir une portion qui dépasse la taille logique demandée.

Il ne faut pas en conclure que toute la zone arrondie peut être utilisée librement.

Le programme ne doit accéder qu'à la région valide.

Les comportements au-delà des limites du fichier peuvent conduire à des fautes mémoire, notamment

Code: Select all

SIGBUS
.


28.

Code: Select all

SIGSEGV
et

Code: Select all

SIGBUS
[/size][/color]

Deux signaux sont particulièrement importants avec

Code: Select all

mmap()
.

Code: Select all

SIGSEGV
[/color]

Typiquement :
  • accès à une adresse non mappée ;
  • violation des permissions de la page ;
  • écriture dans une zone non inscriptible ;
  • exécution dans une zone non exécutable.

Code: Select all

SIGBUS
[/color]

Peut notamment apparaître lorsqu'on accède à une partie d'un mapping dont la correspondance avec le fichier n'est plus valide.

Exemple :
  • fichier tronqué après

    Code: Select all

    mmap()
    ;
  • accès à une page dépassant réellement la fin du fichier.

29. Protections et mode d'ouverture du fichier

Les protections demandées doivent être compatibles avec le

Code: Select all

fd
.

Exemple :

Code: Select all

int fd = open("data.bin", O_RDONLY);
Un mapping privé avec

Code: Select all

PROT_READ
peut fonctionner.

Un mapping

Code: Select all

MAP_SHARED
avec

Code: Select all

PROT_WRITE
exige normalement un

Code: Select all

fd
ouvert de façon compatible avec l'écriture.

Il faut distinguer :
  • protection de la mémoire :

    Code: Select all

    PROT_*
    ;
  • mode d'ouverture du fichier :

    Code: Select all

    O_RDONLY/O_RDWR
    ;
  • type de mapping :

    Code: Select all

    MAP_PRIVATE/MAP_SHARED
    .

30. Flags

Code: Select all

mmap()
supplémentaires[/size][/color]

Linux propose plusieurs

Code: Select all

flags
supplémentaires.

Ils sont généralement spécifiques au système et doivent être utilisés avec prudence.


31.

Code: Select all

MAP_FIXED
[/size][/color]

Code: Select all

MAP_FIXED
demande de placer le mapping exactement à l'adresse indiquée.

Exemple conceptuel :

Code: Select all

mmap(addr,
     size,
     PROT_READ | PROT_WRITE,
     MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
     -1,
     0);
C'est dangereux.

Si une région existe déjà à cette adresse, elle peut être remplacée.

Risques :
  • écraser un mapping existant ;
  • corrompre l'espace d'adressage ;
  • provoquer un crash ;
  • casser des bibliothèques ou structures internes.

Code: Select all

MAP_FIXED
doit donc être réservé aux cas où l'on maîtrise réellement la disposition mémoire.

Approche plus sûre :
  • faire d'abord un mapping normal ;
  • obtenir une zone libre ;
  • ne recourir à

    Code: Select all

    MAP_FIXED
    que si nécessaire.

32.

Code: Select all

MAP_LOCKED
[/size][/color]

Code: Select all

MAP_LOCKED
demande de verrouiller les pages en mémoire d'une manière comparable à

Code: Select all

mlock()
.

Objectif :
réduire les risques que les pages soient déplacées vers le swap.

Cela peut nécessiter des privilèges ou être soumis à des limites de ressources.

Ce flag peut être utile dans des applications temps réel ou sensibles à la latence, mais il ne faut pas verrouiller de grosses quantités de mémoire sans raison.


33.

Code: Select all

MAP_POPULATE
[/size][/color]

Code: Select all

MAP_POPULATE
demande au noyau de précharger les tables/pages du mapping dans certaines conditions.

Objectif :
réduire les page faults futurs lors du premier accès.

Exemple :

Code: Select all

mmap(NULL,
     size,
     PROT_READ,
     MAP_PRIVATE | MAP_POPULATE,
     fd,
     0);
Le coût est déplacé vers le moment de

Code: Select all

mmap()
.

Avantage :
latence plus prévisible lors des accès suivants.

Inconvénient :

Code: Select all

mmap()
peut devenir plus coûteux.


34.

Code: Select all

MAP_NORESERVE
[/size][/color]

Code: Select all

MAP_NORESERVE
influence la réservation de swap pour certains mappings.

Linux peut utiliser l'overcommit : il autorise parfois des allocations virtuelles plus grandes que la RAM + swap réellement disponibles.

Avec

Code: Select all

MAP_NORESERVE
, le noyau peut ne pas réserver immédiatement la capacité de backing nécessaire pour le mapping.

Conséquence :

Code: Select all

mmap()
peut réussir alors qu'il n'existe pas forcément assez de mémoire physique/swap pour matérialiser toutes les pages plus tard.

Si le système manque réellement de mémoire lors de l'utilisation, cela peut conduire à l'intervention de l'OOM killer.


35. Overcommit mémoire sous Linux

Linux permet de réserver virtuellement plus de mémoire que la mémoire physique disponible.

Pourquoi ?

Beaucoup de programmes :
  • réservent de grosses zones sans tout utiliser ;
  • utilisent des pages Copy-On-Write ;
  • n'accèdent jamais à toutes les pages réservées.
Refuser toutes ces réservations au moment de

Code: Select all

mmap()
/

Code: Select all

malloc()
gaspillerait des possibilités.

L'overcommit repose sur l'idée :

mémoire virtuelle réservée != mémoire physique réellement consommée

Exemple :

un programme mappe 10 Go,
mais n'accède qu'à 100 Mo.

Le système ne doit pas nécessairement fournir 10 Go de RAM immédiatement.


36.

Code: Select all

vm.overcommit_memory
[/size][/color]

Sous Linux, la politique d'overcommit est configurable via :

Code: Select all

/proc/sys/vm/overcommit_memory
Valeurs courantes :
  • Code: Select all

    0
    : politique heuristique ;
  • Code: Select all

    1
    : overcommit permissif ;
  • Code: Select all

    2
    : politique stricte basée sur une limite de commit.
La politique exacte dépend du noyau Linux.

On peut consulter :

Code: Select all

cat /proc/sys/vm/overcommit_memory

37.

Code: Select all

vm.overcommit_ratio
[/size][/color]

Lorsque la politique stricte est utilisée, Linux peut tenir compte notamment de :

Code: Select all

/proc/sys/vm/overcommit_ratio
Cette valeur participe au calcul de la quantité de mémoire que le noyau accepte de promettre.

Selon les versions modernes du noyau, d'autres paramètres peuvent également intervenir.

Il ne faut donc pas considérer une formule simplifiée comme universelle pour toutes les versions.


38. OOM killer

OOM signifie Out Of Memory.

Si le système n'a plus assez de mémoire pour satisfaire les besoins réels, Linux peut déclencher l'OOM killer.

Le noyau sélectionne alors un ou plusieurs processus à tuer afin de libérer de la mémoire.

Le choix dépend de plusieurs facteurs internes.

Important :
le fait que

Code: Select all

mmap()
ou

Code: Select all

malloc()
réussisse ne garantit donc pas toujours que toutes les pages pourront être réellement utilisées plus tard sur un système avec overcommit.


39.

Code: Select all

mremap()
[/size][/color]

Code: Select all

mremap()
est une extension Linux.

Elle permet de modifier la taille et éventuellement l'emplacement d'un mapping existant.

Prototype typique :

Code: Select all

#define _GNU_SOURCE
#include <sys/mman.h>

void *mremap(void *old_address,
             size_t old_size,
             size_t new_size,
             int flags,
             ...);
Valeur de retour :
  • succès : nouvelle adresse ;
  • échec :

    Code: Select all

    MAP_FAILED
    .

40. Utilité de

Code: Select all

mremap()
[/size][/color]

Supposons :

Code: Select all

ancienne taille = 4096
nouvelle taille = 8192
On peut vouloir agrandir la région sans refaire manuellement :

Code: Select all

munmap()
+
nouveau mmap()
+
copie des données

Code: Select all

mremap()
peut parfois agrandir le mapping directement.

Exemple :

Code: Select all

void *newp = mremap(p,
                    old_size,
                    new_size,
                    MREMAP_MAYMOVE);

if (newp == MAP_FAILED) {
    perror("mremap");
}

p = newp;

41.

Code: Select all

MREMAP_MAYMOVE
[/size][/color]

Code: Select all

MREMAP_MAYMOVE
autorise le noyau à déplacer le mapping si l'espace situé juste après la région n'est pas disponible.

Sans ce flag, l'agrandissement peut échouer si la région ne peut pas être étendue à son emplacement actuel.

Important :

Après

Code: Select all

mremap()
, si la région a été déplacée, tous les anciens pointeurs absolus vers l'intérieur de la région deviennent invalides.

Exemple dangereux :

Code: Select all

char *base = ...
char *element = base + 100;

base = mremap(...);
Si le mapping bouge,

Code: Select all

element
contient toujours l'ancienne adresse.

Bonne pratique :
stocker des offsets plutôt que des pointeurs absolus lorsqu'un mapping peut être déplacé.


42.

Code: Select all

MREMAP_FIXED
[/size][/color]

Sur Linux,

Code: Select all

MREMAP_FIXED
peut permettre d'imposer une nouvelle adresse.

Il est utilisé avec

Code: Select all

MREMAP_MAYMOVE
.

Comme

Code: Select all

MAP_FIXED
, c'est une opération délicate.

Une mauvaise adresse peut remplacer une région existante.

À réserver aux cas avancés.


43. Mappings non linéaires

Un mapping classique est linéaire.

Exemple :

Code: Select all

adresse virtuelle page 0 -> fichier page 0
adresse virtuelle page 1 -> fichier page 1
adresse virtuelle page 2 -> fichier page 2
Certaines anciennes interfaces Linux permettaient de réarranger l'association entre pages virtuelles et pages de fichier.

Exemple conceptuel :

Code: Select all

VMA page 0 -> fichier page 2
VMA page 1 -> fichier page 1
VMA page 2 -> fichier page 0
L'appel

Code: Select all

remap_file_pages()
a historiquement servi à cela.


44.

Code: Select all

remap_file_pages()
[/size][/color]

Prototype historique :

Code: Select all

int remap_file_pages(void *addr,
                     size_t size,
                     int prot,
                     size_t pgoff,
                     int flags);
Cette interface est spécifique Linux.

Elle permettait de créer des mappings non linéaires sans multiplier les VMA.

Cependant, dans les Linux modernes,

Code: Select all

remap_file_pages()
est obsolète/dépréciée et son comportement a évolué.

Pour du nouveau code, il vaut mieux éviter de bâtir une architecture autour de cette API.

Le concept reste néanmoins important pour comprendre le chapitre et l'évolution du sous-système mémoire Linux.


45. VMA : Virtual Memory Area

Le noyau représente les différentes régions de l'espace virtuel d'un processus sous forme de zones mémoire virtuelles.

On parle souvent de VMA : Virtual Memory Area.

Une VMA décrit une région ayant des caractéristiques communes :
  • adresses de début et de fin ;
  • permissions ;
  • type de mapping ;
  • fichier associé éventuellement ;
  • Code: Select all

    offset
    ;
  • Code: Select all

    flags
    .
Les mappings créés avec

Code: Select all

mmap()
sont donc intégrés dans la structure mémoire du processus.


46. Observer les mappings d'un processus

Sous Linux :

Code: Select all

/proc/PID/maps
Exemple :

Code: Select all

cat /proc/self/maps
ou :

Code: Select all

cat /proc/1234/maps
On peut y observer :
  • plages d'adresses ;
  • permissions ;
  • Code: Select all

    offset
    ;
  • device ;
  • inode ;
  • fichier associé.
Exemple de permissions :

Code: Select all

r-xp

r = readable
w = writable
x = executable
p = private
s = shared
Cette interface est extrêmement utile pour comprendre

Code: Select all

mmap()
en pratique.


47. Exemple d'entrée

Code: Select all

/proc/PID/maps
[/size][/color]

Exemple fictif :

Code: Select all

7f1234000000-7f1234021000 rw-p 00000000 00:00 0
Signification :
  • 7f1234000000 : adresse de début ;
  • 7f1234021000 : adresse de fin ;
  • Code: Select all

    rw-p
    : lecture + écriture, privé ;
  • 00000000 :

    Code: Select all

    offset
    ;
  • 00:00 : device ;
  • 0 : inode.
Une bibliothèque peut apparaître ainsi :

Code: Select all

7f...-7f... r-xp ... /usr/lib/libc.so.6

48. Relation avec les exécutables et bibliothèques

Lorsqu'un programme est lancé, Linux mappe plusieurs segments dans l'espace virtuel :
  • code ;
  • données ;
  • bibliothèques partagées ;
  • heap ;
  • stack ;
  • zones anonymes ;
  • vdso/vvar selon l'architecture.
Les segments exécutables sont généralement mappés avec des permissions proches de :

Code: Select all

r-x
Les données modifiables :

Code: Select all

rw-
Les mécanismes de

Code: Select all

mmap()
sont donc fondamentaux pour le fonctionnement normal d'un processus.


49.

Code: Select all

mmap()
et

Code: Select all

malloc()
[/size][/color]

Code: Select all

malloc()
est une API de bibliothèque C.

Code: Select all

mmap()
est une interface système de mémoire virtuelle.

Code: Select all

malloc()
peut obtenir de la mémoire de différentes manières, par exemple : Donc :

Code: Select all

malloc() != mmap()
Mais

Code: Select all

malloc()
peut utiliser

Code: Select all

mmap()
en interne.


50.

Code: Select all

mmap()
comme primitive d'allocation[/size][/color]

Exemple :

Code: Select all

size_t size = 4096;

int *array = mmap(NULL,
                  size,
                  PROT_READ | PROT_WRITE,
                  MAP_PRIVATE | MAP_ANONYMOUS,
                  -1,
                  0);

if (array == MAP_FAILED) {
    perror("mmap");
}

array[0] = 42;

munmap(array, size);
Avantage :
contrôle direct sur :
  • permissions ;
  • taille ;
  • type de mapping ;
  • partage ;
  • adresse ;
  • Code: Select all

    flags
    .
Inconvénient :
beaucoup moins pratique qu'un allocateur généraliste pour de petites allocations.


51.

Code: Select all

mmap()
et ASLR[/size][/color]

L'ASLR randomise certaines adresses de l'espace virtuel.

Lorsque

Code: Select all

addr = NULL
, le noyau choisit l'adresse en fonction de la disposition actuelle de l'espace d'adressage et des politiques du système.

Il ne faut donc jamais supposer qu'un

Code: Select all

mmap(NULL, ...)
retournera toujours la même adresse.

C'est une raison supplémentaire pour éviter les pointeurs absolus persistants.


52. Fermeture du

Code: Select all

fd
après

Code: Select all

mmap()
[/size][/color]

Exemple :

Code: Select all

int fd = open(...);

void *p = mmap(..., fd, 0);

close(fd);
Le mapping continue à fonctionner si

Code: Select all

mmap()
a réussi.

Pourquoi ?

Le noyau maintient les références internes nécessaires au mapping.

Le

Code: Select all

fd
n'est qu'un moyen initial de désigner l'objet fichier pendant la création du mapping.


53. Troncature d'un fichier mappé

Attention à

Code: Select all

ftruncate()
.

Si un fichier est réduit alors qu'une partie supérieure est encore mappée, l'accès à une région qui n'existe plus réellement dans le fichier peut provoquer

Code: Select all

SIGBUS
.

Exemple :

Code: Select all

mapping : 0 -> 1 Mo
ftruncate(fd, 4096)
Accéder ensuite à

Code: Select all

addr[500000]
est dangereux.

Il faut coordonner les opérations de redimensionnement avec les utilisateurs du mapping.


54. Agrandir un fichier avant un mapping writable

Si on veut créer un fichier de 1 Mo puis le mapper :

Code: Select all

int fd = open("data.bin", O_RDWR | O_CREAT, 0644);

if (ftruncate(fd, 1024 * 1024) == -1) {
    perror("ftruncate");
}

char *p = mmap(NULL,
               1024 * 1024,
               PROT_READ | PROT_WRITE,
               MAP_SHARED,
               fd,
               0);
Il faut s'assurer que le fichier a bien la taille requise avant d'écrire dans les pages correspondantes.


55. Synchronisation inter-processus

Code: Select all

MAP_SHARED
ne remplace pas un mécanisme de synchronisation.

Exemple dangereux :

Code: Select all

Processus A :
counter++;

Processus B :
counter++;
Si les deux exécutent cela simultanément, il peut y avoir une race condition.

Code: Select all

counter++
n'est pas une opération atomique générale.

Solutions :
  • sémaphore System V ;
  • sémaphore POSIX ;
  • Code: Select all

    pthread_mutex_t
    avec

    Code: Select all

    PTHREAD_PROCESS_SHARED
    ;
  • atomiques C ;
  • futex ;
  • autres primitives appropriées.

56. Mutex partagé dans

Code: Select all

mmap()
[/size][/color]

Un

Code: Select all

pthread_mutex_t
peut être placé dans une région

Code: Select all

MAP_SHARED
.

Il faut configurer son attribut :

Code: Select all

pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
Ainsi plusieurs processus peuvent utiliser le mutex.

Exemple structure :

Code: Select all

struct Shared {
    pthread_mutex_t mutex;
    int counter;
};
Puis cette structure peut être créée dans un mapping partagé.


57. Exemple : fichier partagé entre processus

Processus A :

Code: Select all

int fd = open("shared.bin", O_RDWR);

int *p = mmap(NULL,
              sizeof(int),
              PROT_READ | PROT_WRITE,
              MAP_SHARED,
              fd,
              0);

*p = 123;

msync(p, sizeof(int), MS_SYNC);
Processus B mappe le même fichier et peut lire la valeur.

C'est un mécanisme IPC persistant puisque le fichier continue d'exister après la fin des processus.


58. Mapping anonyme vs shared memory POSIX/System V

Mapping anonyme

Code: Select all

MAP_SHARED
:
  • très pratique entre processus apparentés après

    Code: Select all

    fork()
    ;
  • pas de nom global ;
  • pas d'objet fichier classique.
POSIX shared memory : System V shared memory : Dans les trois cas, l'idée fondamentale est de rendre les mêmes données accessibles dans plusieurs espaces d'adressage.


59.

Code: Select all

mmap()
+

Code: Select all

shm_open()
[/size][/color]

Exemple conceptuel POSIX :

Code: Select all

int fd = shm_open("/demo",
                  O_CREAT | O_RDWR,
                  0600);

ftruncate(fd, 4096);

void *p = mmap(NULL,
               4096,
               PROT_READ | PROT_WRITE,
               MAP_SHARED,
               fd,
               0);
La shared memory POSIX repose donc directement sur

Code: Select all

mmap()
pour établir la vue dans l'espace virtuel du processus.


60. Protection temporaire avec

Code: Select all

PROT_NONE
[/size][/color]

Une technique utile consiste à créer ou modifier une région en

Code: Select all

PROT_NONE
.

Exemple :

Code: Select all

mmap(NULL,
     size,
     PROT_NONE,
     MAP_PRIVATE | MAP_ANONYMOUS,
     -1,
     0);
Toute tentative d'accès provoque une faute.

Cela permet notamment :
  • guard pages ;
  • détection de débordement de stack ;
  • zones réservées mais non accessibles.
Les protections peuvent ensuite être modifiées avec

Code: Select all

mprotect()
, étudié dans le chapitre suivant.


61. W^X : Write XOR Execute

En sécurité moderne, on évite idéalement les pages simultanément :

Code: Select all

PROT_WRITE | PROT_EXEC
Une politique W^X préfère qu'une page soit :
  • écrivable ;
  • ou exécutable ;
  • mais pas les deux simultanément.
Les JIT utilisent souvent une séquence du type :

Code: Select all

écriture :
PROT_READ | PROT_WRITE

puis :

mprotect(..., PROT_READ | PROT_EXEC)
Cela réduit certaines possibilités d'exploitation.


62. Erreurs fréquentes
  • tester

    Code: Select all

    mmap()
    contre

    Code: Select all

    NULL
    au lieu de

    Code: Select all

    MAP_FAILED
    ;
  • oublier

    Code: Select all

    munmap()
    ;
  • utiliser un

    Code: Select all

    offset
    non aligné ;
  • écrire dans un mapping

    Code: Select all

    PROT_READ
    ;
  • confondre

    Code: Select all

    MAP_PRIVATE
    et

    Code: Select all

    MAP_SHARED
    ;
  • croire que

    Code: Select all

    MAP_SHARED
    fournit une synchronisation ;
  • accéder après

    Code: Select all

    munmap()
    ;
  • utiliser un pointeur ancien après

    Code: Select all

    mremap()
    ;
  • mapper au-delà de la taille du fichier et accéder à des pages invalides ;
  • oublier

    Code: Select all

    ftruncate()
    avant de mapper un nouveau fichier writable ;
  • utiliser

    Code: Select all

    MAP_FIXED
    sans maîtriser les conséquences ;
  • supposer que

    Code: Select all

    mmap()
    charge immédiatement toutes les pages en RAM ;
  • supposer qu'un

    Code: Select all

    mmap()
    réussi garantit que toute la mémoire pourra être matérialisée ;
  • oublier de vérifier les valeurs de retour.

63. Lazy allocation et page faults

Un

Code: Select all

mmap()
réussi ne signifie pas nécessairement que toutes les pages sont immédiatement présentes en RAM.

Linux utilise largement le chargement à la demande.

Exemple :

Code: Select all

void *p = mmap(NULL,
               1ULL << 30,
               PROT_READ | PROT_WRITE,
               MAP_PRIVATE | MAP_ANONYMOUS,
               -1,
               0);
Le processus a réservé 1 Go d'espace virtuel.

Mais toutes les pages ne sont pas nécessairement allouées physiquement immédiatement.

Lors du premier accès :

Code: Select all

p[0] = 1;
une page fault peut survenir.

Le noyau crée ou associe alors la page nécessaire.

Page fault ne signifie donc pas forcément erreur.

Il existe des page faults normaux faisant partie du fonctionnement de la mémoire virtuelle.


64. Minor et major page faults

De manière simplifiée :

Minor fault :
la page peut être obtenue sans lecture disque coûteuse.

Major fault :
le système doit typiquement récupérer les données depuis un stockage.

Code: Select all

mmap()
s'appuie fortement sur ce mécanisme de fautes de page.


65. Quand utiliser

Code: Select all

mmap()
[/size][/color]

Code: Select all

mmap()
est particulièrement intéressant pour :
  • gros fichiers ;
  • accès aléatoires ;
  • bases de données ;
  • structures directement représentées en mémoire ;
  • IPC par mémoire partagée ;
  • chargement d'exécutables/bibliothèques ;
  • allocations mémoire avancées ;
  • fichiers consultés très souvent ;
  • situations où l'on veut contrôler les permissions des pages.

Code: Select all

read()/write()
peuvent être préférables pour :
  • petits fichiers ;
  • I/O séquentielles simples ;
  • protocoles de streaming ;
  • code où la simplicité prime ;
  • cas où l'on veut contrôler précisément chaque opération d'I/O.

66. Comparaison

Code: Select all

mmap()
/

Code: Select all

read()
[/size][/color]

Code: Select all

read() :

fd -> read() -> buffer utilisateur

mmap() :

fd -> mapping -> accès mémoire

Code: Select all

read()
:
  • explicite ;
  • facile à raisonner ;
  • très adapté au streaming ;
  • contrôle direct des tailles lues.

Code: Select all

mmap()
:
  • accès via pointeurs ;
  • intégration avec mémoire virtuelle ;
  • très pratique pour accès aléatoire ;
  • peut simplifier certaines applications.

67. Nettoyage correct

Exemple général :

Code: Select all

int fd = open(...);

if (fd == -1)
    ...

void *addr = mmap(...);

if (addr == MAP_FAILED) {
    close(fd);
    ...
}

close(fd);

/* utilisation */

if (msync(...) == -1)
    ...

if (munmap(addr, length) == -1)
    ...
Toujours gérer les erreurs et les ressources dans le bon ordre.


68. Exemple complet : modifier un fichier avec

Code: Select all

MAP_SHARED
[/size][/color]

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>

int main(void)
{
    const char *path = "data.txt";

    int fd = open(path, O_RDWR);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    struct stat st;

    if (fstat(fd, &st) == -1) {
        perror("fstat");
        close(fd);
        return EXIT_FAILURE;
    }

    if (st.st_size == 0) {
        fprintf(stderr, "Fichier vide\n");
        close(fd);
        return EXIT_FAILURE;
    }

    char *addr = mmap(NULL,
                      st.st_size,
                      PROT_READ | PROT_WRITE,
                      MAP_SHARED,
                      fd,
                      0);

    if (addr == MAP_FAILED) {
        perror("mmap");
        close(fd);
        return EXIT_FAILURE;
    }

    close(fd);

    addr[0] = 'X';

    if (msync(addr, st.st_size, MS_SYNC) == -1) {
        perror("msync");
        munmap(addr, st.st_size);
        return EXIT_FAILURE;
    }

    if (munmap(addr, st.st_size) == -1) {
        perror("munmap");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

69. Exemple complet : mapping anonyme redimensionné avec

Code: Select all

mremap()
[/size][/color]

Code: Select all

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>

int main(void)
{
    size_t old_size = 4096;
    size_t new_size = 8192;

    int *p = mmap(NULL,
                  old_size,
                  PROT_READ | PROT_WRITE,
                  MAP_PRIVATE | MAP_ANONYMOUS,
                  -1,
                  0);

    if (p == MAP_FAILED) {
        perror("mmap");
        return EXIT_FAILURE;
    }

    p[0] = 42;

    void *tmp = mremap(p,
                       old_size,
                       new_size,
                       MREMAP_MAYMOVE);

    if (tmp == MAP_FAILED) {
        perror("mremap");
        munmap(p, old_size);
        return EXIT_FAILURE;
    }

    p = tmp;

    printf("%d\n", p[0]);

    if (munmap(p, new_size) == -1) {
        perror("munmap");
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

70. Carte mentale du chapitre

Code: Select all

mmap()
|
+-- source
|   |
|   +-- fichier
|   |
|   +-- anonyme
|
+-- visibilité
|   |
|   +-- MAP_PRIVATE
|   |      |
|   |      +-- Copy-On-Write
|   |
|   +-- MAP_SHARED
|          |
|          +-- IPC
|          +-- fichier partagé
|
+-- protections
|   |
|   +-- PROT_NONE
|   +-- PROT_READ
|   +-- PROT_WRITE
|   +-- PROT_EXEC
|
+-- gestion
|   |
|   +-- munmap()
|   +-- msync()
|   +-- mremap()
|
+-- flags Linux
    |
    +-- MAP_ANONYMOUS
    +-- MAP_FIXED
    +-- MAP_LOCKED
    +-- MAP_POPULATE
    +-- MAP_NORESERVE

71. Tableau mental des combinaisons principales

Mapping fichier +

Code: Select all

MAP_PRIVATE
:
  • lecture/traitement privé d'un fichier ;
  • modifications non propagées comme écritures normales vers le fichier ;
  • Copy-On-Write.
Mapping fichier +

Code: Select all

MAP_SHARED
:
  • memory-mapped I/O ;
  • IPC ;
  • modifications partageables ;
  • possibilité de synchronisation vers le fichier.
Mapping anonyme +

Code: Select all

MAP_PRIVATE
:
  • allocation mémoire privée ;
  • pages initialisées à zéro.
Mapping anonyme +

Code: Select all

MAP_SHARED
:
  • mémoire partagée ;
  • très pratique après

    Code: Select all

    fork()
    .

72. API à connaître absolument

À retenir pour ce chapitre :
  • Code: Select all

    mmap()
    : créer un mapping ;
  • Code: Select all

    munmap()
    : supprimer un mapping ;
  • Code: Select all

    msync()
    : synchroniser un mapping partagé avec son backing file ;
  • Code: Select all

    mremap()
    : redimensionner/déplacer un mapping sur Linux ;
  • Code: Select all

    open()
    : ouvrir le fichier ;
  • Code: Select all

    close()
    : fermer le descripteur ;
  • Code: Select all

    fstat()
    : récupérer la taille du fichier ;
  • Code: Select all

    ftruncate()
    : modifier la taille du fichier ;
  • Code: Select all

    sysconf(_SC_PAGESIZE)
    : obtenir la taille de page ;
  • Code: Select all

    fork()
    : hériter des mappings dans un processus fils.

73. Constantes à connaître

Protections : Types : Mappings anonymes : Autres

Code: Select all

flags
:

Code: Select all

msync
:

Code: Select all

mremap
:
74. Ce qu'il faut savoir expliquer sans documentation

Après ce chapitre, il faut être capable d'expliquer :
  • ce qu'est un mapping mémoire ;
  • la différence entre mapping fichier et anonyme ;
  • la différence

    Code: Select all

    MAP_PRIVATE
    /

    Code: Select all

    MAP_SHARED
    ;
  • le Copy-On-Write ;
  • le rôle de

    Code: Select all

    PROT_READ/WRITE/EXEC/NONE
    ;
  • les six paramètres de

    Code: Select all

    mmap()
    ;
  • pourquoi

    Code: Select all

    mmap()
    retourne

    Code: Select all

    MAP_FAILED
    ;
  • pourquoi l'

    Code: Select all

    offset
    doit être aligné ;
  • le rôle de

    Code: Select all

    munmap()
    ;
  • le rôle de

    Code: Select all

    msync()
    ;
  • comment partager de la mémoire parent/enfant avec

    Code: Select all

    fork()
    ;
  • pourquoi

    Code: Select all

    MAP_SHARED
    ne remplace pas un sémaphore/mutex ;
  • le principe des page faults ;
  • le principe de l'overcommit ;
  • le risque de l'OOM killer ;
  • le rôle de

    Code: Select all

    MAP_NORESERVE
    ;
  • le danger de

    Code: Select all

    MAP_FIXED
    ;
  • le rôle de

    Code: Select all

    MAP_POPULATE
    ;
  • le rôle de

    Code: Select all

    MAP_LOCKED
    ;
  • le fonctionnement de

    Code: Select all

    mremap()
    ;
  • pourquoi un pointeur peut devenir invalide après

    Code: Select all

    mremap()
    ;
  • la différence générale entre

    Code: Select all

    mmap()
    et

    Code: Select all

    read()/write()
    .

75. Mini questions de révision

1. Que retourne

Code: Select all

mmap()
en cas d'échec ?

Code: Select all

MAP_FAILED
.

2.

Code: Select all

mmap()
retourne-t-il forcément

Code: Select all

NULL
en cas d'erreur ?

Non.

3. Quel flag choisir pour partager les modifications ?

Code: Select all

MAP_SHARED
.

4. Quel flag crée une vue privée ?

Code: Select all

MAP_PRIVATE
.

5. Quelle technique est utilisée par

Code: Select all

MAP_PRIVATE
lors des écritures ?

Copy-On-Write.

6. Quel flag permet de créer une zone sans fichier ?

Code: Select all

MAP_ANONYMOUS
.

7. Quelle valeur de

Code: Select all

fd
utilise-t-on généralement avec

Code: Select all

MAP_ANONYMOUS
?

Code: Select all

-1
.

8. Quelle valeur d'

Code: Select all

offset
utilise-t-on généralement avec

Code: Select all

MAP_ANONYMOUS
?

Code: Select all

0
.

9. Quel appel détruit un mapping ?

Code: Select all

munmap()
.

10. Quel appel demande la synchronisation avec le fichier ?

Code: Select all

msync()
.

11. Quel flag

Code: Select all

msync
attend la synchronisation ?

Code: Select all

MS_SYNC
.

12.

Code: Select all

mmap()
charge-t-il nécessairement toutes les pages en RAM immédiatement ?

Non.

13. Un page fault est-il toujours une erreur ?

Non.

14. Que se passe-t-il avec

Code: Select all

MAP_SHARED
après

Code: Select all

fork()
?

Parent et enfant peuvent partager les mêmes pages.

15.

Code: Select all

MAP_SHARED
empêche-t-il les races ?

Non.

16. Quelle API Linux peut redimensionner un mapping ?

Code: Select all

mremap()
.

17. Quel flag autorise

Code: Select all

mremap()
à déplacer le mapping ?

Code: Select all

MREMAP_MAYMOVE
.

18.

Code: Select all

MAP_FIXED
est-il dangereux ?

Oui, car il peut remplacer des mappings existants.

19. À quoi sert

Code: Select all

MAP_POPULATE
?

À précharger/préparer des pages afin de réduire certains page faults ultérieurs.

20. Que signifie l'overcommit ?

Le système peut accepter plus de mémoire virtuelle promise que la mémoire physique/swap immédiatement disponible.


Résumé final

Code: Select all

mmap()
est l'une des primitives fondamentales de la mémoire virtuelle sous Linux.

Il permet de transformer une ressource — fichier ou mémoire anonyme — en une région directement accessible dans l'espace virtuel du processus.

Les deux distinctions essentielles sont :

Code: Select all

MAP_PRIVATE
utilise un comportement Copy-On-Write pour les modifications privées.

Code: Select all

MAP_SHARED
rend les changements visibles entre processus partageant les mêmes pages et permet, dans le cas d'un fichier, de réaliser du memory-mapped I/O.

Code: Select all

PROT_READ
,

Code: Select all

PROT_WRITE
,

Code: Select all

PROT_EXEC
et

Code: Select all

PROT_NONE
définissent les droits d'accès.

Code: Select all

munmap()
retire une région de l'espace virtuel.

Code: Select all

msync()
sert à demander la synchronisation d'un mapping partagé avec son backing file.

Code: Select all

MAP_ANONYMOUS
permet de créer directement des régions mémoire initialisées à zéro.

Les mappings

Code: Select all

MAP_SHARED
anonymes sont particulièrement utiles pour partager de la mémoire entre un parent et son enfant après

Code: Select all

fork()
.

Linux fournit aussi des extensions telles que

Code: Select all

MAP_POPULATE
,

Code: Select all

MAP_LOCKED
,

Code: Select all

MAP_NORESERVE
,

Code: Select all

MAP_FIXED
et

Code: Select all

mremap()
.

L'overcommit signifie qu'une allocation virtuelle réussie n'implique pas que toute la mémoire correspondante existe immédiatement en RAM ou en swap.

En cas de pénurie mémoire réelle, Linux peut déclencher l'OOM killer.

Enfin,

Code: Select all

mmap()
ne remplace pas la synchronisation : une mémoire partagée reste une section critique potentielle dès que plusieurs processus ou threads la modifient.

Who is online

Users browsing this forum: No registered users and 1 guest