Quel est le but de l’indicateur MAP_ANONYMOUS dans l’appel système mmap?

De la page de manuel,

 MAP_ANONYMOUS The mapping is not backed by any file; its contents are initialized to zero. The fd and offset arguments are ignored; however, some implementations require fd to be -1 if MAP_ANONYMOUS (or MAP_ANON) is specified, and portable applications should ensure this. The use of MAP_ANONYMOUS in conjunction with MAP_SHARED is only supported on Linux since kernel 2.4. 

Quel est le but de l’utilisation de MAP_ANONYMOUS ? Tout exemple serait bien. Aussi d’où la mémoire sera cartographiée?

Il est écrit sur la page de manuel que The use of MAP_ANONYMOUS in conjunction with MAP_SHARED is only supported on Linux since kernel 2.4. Comment puis-je partager la mémoire mappée avec MAP_ANONYMOUS avec un autre processus?

Les mappages anonymes peuvent être représentés sous la forme d’un fichier virtuel mis à zéro. Les mappages anonymes sont simplement de gros blocs de mémoire remplis de zéro, prêts à être utilisés. Ces mappages résident en dehors du tas, ne consortingbuent donc pas à la fragmentation du segment de données.

MAP_ANONYMOUS + MAP_PRIVATE:

  • chaque appel crée une cartographie distincte
  • les enfants héritent des mappages des parents
  • les écritures des enfants sur le mappage hérité sont traitées de manière copiée
  • le principal objective de ce type de mappage est d’allouer une nouvelle mémoire remise à zéro
  • malloc utilise des mappages privés anonymes pour traiter les demandes d’allocation de mémoire supérieures aux octets MMAP_THRESHOLD.
    MMAP_THRESHOLD est généralement de 128 Ko.

MAP_ANONYMOUS + MAP_SHARED:

  • eah call crée un mappage distinct qui ne partage aucune page avec aucun autre mappage
  • les enfants héritent des mappages des parents
  • pas de copie sur écriture lorsque quelqu’un partage le mappage écrit sur le mappage partagé
  • les mappages anonymes partagés autorisent l’IPC d’une manière similaire aux segments de mémoire System V, mais uniquement entre les processus associés

Sous Linux, il existe deux manières de créer des mappages anonymes:

  • spécifie l’indicateur MAP_ANONYMOUS et passe -1 pour fd

      addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (addr == MAP_FAILED) exit(EXIT_FAILURE); 
  • ouvrir / dev / zero et passer cette fd ouverte

      fd = open("/dev/zero", O_RDWR); addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); 

    (cette méthode est généralement utilisée sur des systèmes tels que BSD, qui n’ont pas d’indicateur MAP_ANONYMOUS)

Avantages des mappages anonymes:
– pas de fragmentation de l’espace d’adressage virtuel; après le démappage, la mémoire est immédiatement renvoyée au système
– ils sont modifiables en termes de taille d’allocation, d’permissions et ils peuvent aussi recevoir des conseils comme les mappages normaux
– chaque allocation est un mappage distinct, distinct du tas global

Inconvénients des mappages anonymes:
– la taille de chaque mappage est un multiple entier de la taille de la page du système, ce qui peut entraîner un gaspillage de l’espace d’adressage
– la création et le retour de mappages entraînent une surcharge supérieure à celle du tas pré-alloué

Si un programme contenant un tel mappage transforme un processus, l’enfant hérite du mappage. Le programme suivant montre ce type d’inheritance:

 #ifdef USE_MAP_ANON #define _BSD_SOURCE #endif #include  #include  #include  #include  #include  #include  #include  int main(int argc, char *argv[]) { /*Pointer to shared memory region*/ int *addr; #ifdef USE_MAP_ANON /*Use MAP_ANONYMOUS*/ addr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); if (addr == MAP_FAILED) { fprintf(stderr, "mmap() failed\n"); exit(EXIT_FAILURE); } #else /*Map /dev/zero*/ int fd; fd = open("/dev/zero", O_RDWR); if (fd == -1) { fprintf(stderr, "open() failed\n"); exit(EXIT_FAILURE); } addr = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { fprintf(stderr, "mmap() failed\n"); exit(EXIT_FAILURE); } if (close(fd) == -1) { /*No longer needed*/ fprintf(stderr, "close() failed\n"); exit(EXIT_FAILURE); } #endif *addr = 1; /*Initialize integer in mapped region*/ switch(fork()) { /*Parent and child share mapping*/ case -1: fprintf(stderr, "fork() failed\n"); exit(EXIT_FAILURE); case 0: /*Child: increment shared integer and exit*/ printf("Child started, value = %d\n", *addr); (*addr)++; if (munmap(addr, sizeof(int)) == -1) { fprintf(stderr, "munmap()() failed\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); default: /*Parent: wait for child to terminate*/ if (wait(NULL) == -1) { fprintf(stderr, "wait() failed\n"); exit(EXIT_FAILURE); } printf("In parent, value = %d\n", *addr); if (munmap(addr, sizeof(int)) == -1) { fprintf(stderr, "munmap()() failed\n"); exit(EXIT_FAILURE); } exit(EXIT_SUCCESS); } 

Sources:
L’interface de programmation Linux
Chapitre 49: Mappages de mémoire,
Auteur: Michael Kerrisk

Programmation Linux (3ème édition)
Chapitre 8: Gestion de la mémoire,
Auteur: Robert Love