Mémoires partagées

Guillaume Chanel

Memory-mapped file (Fichier en mémoire partagée)

Rappel

Nous avons vu que certaines pages de la mémoire virtuelle:

  • ne sont pas présentes en mémoire physique mais réside sur des systèmes de fichiers (swap / fichiers executables)
  • deviennent disponibles au fur et à mesure des fautes de pages
  • sont partagées entre plusieurs processus (e.g. librairies partagées)
  • peuvent être partagées uniquement jusqu'à leur modification ("copy-on-write")

Nous allons voir un appel système qui permet d'associer un segment de mémoire virtuelle à un segment de fichier. Cet appel système est par exemple utilisé pour charger les librairies partagées.

Principe du "file mapping"

Associer le segment (une partie) d'un fichier à un nouveau segments de mémoire partagé (file mapping). Cela permet de:

  • partager des pages (données, instructions) entre plusieurs processus;
  • accéder aux données directement en mémoire (i.e. par pointeurs) plutôt que dans un fichier (i.e. par curseur)

Un tel fichier ne sera pas chargé intégralement en mémoire mais page par page au fur et a mesure des fautes de page du processus.

Principe du "file mapping"

Deux processus peuvent partager un segment en y associant des espaces d’adressage virtuel différents:

Shared memory

Le "file mapping" en pratique

Pour associer un fichier à un espace de la mémoire virtuelle du processus on:

  • ouvre le fichier en lecture et/ou écriture pour obtenir un descripteur de fichier fd:
  • int open(const char *pathname, int flags);
  • associe le descripteur de fichier à une zone de la mémoire virtuelle par un appel à:
  • void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
  • pense ensuite à fermer / désassocier la mémoire partagée:
  • int munmap(void *addr, size_t length);
  • ferme le fichier:
  • int close(int fd);

open / close

On peut ouvrir un fichier avec l'appel système suivant:


								int open(const char *pathname, int flags);
							
  • pathname est le nom du fichier;
  • flags est un champ de bit indiquant le mode d'accès au fichier (O_RDONLY, O_WRONLY, O_RDWR);
  • retourne un entier représentant le fichier (descripteur de fichier), soit -1 en cas d'erreur (vérifier errno).

On doit fermer un fichier avec l'appel système suivant:


								int close(int fd);
							
  • fd est l'entier représentant le fichier (descripteur de fichier);
  • 0 en cas de succès, -1 en cas d'erreur (vérifier errno).

Note: la description de la fonction open est incomplète. Une description complète sera présentée plus tard.

mmap


							#include <sys/mman.h>
							void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
						
  • fd: entier représentant le fichier (file descriptor);
  • addr: adresse d‘un début de page (ajustée automatiquement), si NULL l'adresse est choisie automatiquement;
  • offset: début du mapping dans le fichier, doit être multiple de la taille d'une page
  • length: taille du mapping dans le fichier, complété par des zéros pour remplir une page en mémoire
  • retourne l'adresse virtuelle correspondant au début du segment
File mapping

mmap


							#include <sys/mman.h>
							void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
						

prot, bit field définissant la protection des pages partagées:

  • PROT_READ / PROT_WRITE / PROT_EXEC autorise respectivement la lecture, l’écriture et l’exécution;
  • PROT_NONE aucun droit, utilisé pour réserver des pages;
  • les droits doivent correspondre au mode d’ouverture du fichier.

flags, bit field utilisé pour les options suivantes:

  • MAP_SHARED: la zone est partagée entre les processus / fichiers toute modification sera reportée aux autres processus et dans le fichier;
  • MAP_PRIVATE: copy-on-write, si un processus modifie le contenu il crée sa propre copie des pages et le fichier ne sera pas modifié;
  • MAP_ANONYMOUS: pas d’association avec un fichier, la mémoire est initialisée à 0 (fd et offset sont ignorés) et partageable uniquement avec ses enfants.

Exercice

L'objectif de cet exercice est de créer un processus qui:

  • charge le segment contenant le code d'un autre executable dans sa mémoire virtuelle,
  • execute ce code à partir du point d'entrée mentionné dans l'exécutable.

On simule ainsi l'exécution d'un code externe un peu comme cela est fait pour une librairie partagée

Exercice

Le programme à charger est fournit en assembleur. Il faut le compiler avec nasm (voir Makefile).

Exercice

Il faut donc:

  • Compiler le programme simple,
  • Executer ce programme et confirmer son status de retour (42),
  • utiliser la commande readelf pour identifier le segment à charger et le point d'entrée du programme,
  • implementer un programme en C qui va:
    • définir les adresses mesurées à l'étape précédente avec des #define
    • charger le bon segment avec mmap,
    • définir une fonction pointant sur le point d'entrée dans le mémoire virtuelle,
    • exécuter cette fonction
  • tester que votre programme C retourne bien 42

Pour aller plus loin: votre programme C devra rechercher les informations du segment et du point d'entrée directement dans le fichier executable.

Exerice - correction