Aller au contenu

Shared memory

Mémoire partagée¤

Comme nous le verrons plus loin dans le chapitre consacré à la MMU, la mémoire d’un processus (un programme en cours d’exécution) est isolée de celle des autres. Le système d’exploitation fait respecter cette séparation pour éviter tout accès non autorisé.

Lorsque l'on souhaite communiquer entre plusieurs programmes, il est possible d'utiliser différentes méthodes :

  • les flux (fichiers, stdin, stdout...)
  • la mémoire partagée
  • les sockets

Vous avez déjà vu les flux au chapitre précédent, et les sockets ne font pas partie de ce cours d'introduction.

Notons que la mémoire partagée est un mécanisme propre à chaque système d’exploitation. Sous POSIX, elle est normalisée : un programme compatible POSIX et utilisant la mémoire partagée pourra fonctionner sous Linux, WSL ou macOS, mais pas nécessairement sous Windows.

C’est principalement l’appel système mmap qui est utilisé. Il permet de mapper ou de démapper des fichiers ou des périphériques en mémoire.

void *mmap(
    void *addr,
    size_t length, // Size in bytes
    int prot,      // Access protection (read/write/execute)
    int flags,     // Attributs (shared/private/anonymous...)
    int fd,
    int offset
);

Voici un exemple permettant de réserver un espace partagé en écriture et en lecture entre deux processus :

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

void* create_shared_memory(size_t size) {
    // Accessible en lecture et écriture
    int protection = PROT_READ | PROT_WRITE;

    // D'autres processus peuvent accéder à cet espace
    // lequel est anonyme
    // so only this process and its children will be able to use it:
    int visibility = MAP_SHARED | MAP_ANONYMOUS;

    // The remaining parameters to `mmap()` are not important for this use case,
    // but the manpage for `mmap` explains their purpose.
    return mmap(NULL, size, protection, visibility, -1, 0);
}

File memory mapping¤

Traditionnellement, lorsqu’on souhaite travailler sur un fichier, on l’ouvre avec fopen puis on lit son contenu. Au besoin, ce fichier est copié en mémoire :

FILE *fp = fopen("foo", "r");
fseek(fp, 0, SEEK_END);
int filesize = ftell(fp);
fseek(fp, 0, SEEK_SET);
char *file = malloc(filesize);
fread(file, filesize, sizeof(char), fp);
fclose(fp);

Cette copie n’est pas toujours indispensable. Une approche POSIX — non couverte par le standard C99 — consiste à lier le fichier dans un espace mémoire partagé.

Ceci nécessite l'utilisation de fonctions bas niveau.

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

int main() {
    int fd = open("foo.txt", O_RDWR, 0600);
    char *addr = mmap(NULL, 100, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    printf("Espace mappé à %p\n", addr);
    printf("Premiers caractères du fichiers : %.*s...\n", 20, addr);
}

Les avantages de cette méthode sont les suivants :

  • pas nécessaire de copier l'intégralité du fichier en mémoire ;
  • possibilité de partager le même fichier ouvert entre plusieurs processus ;
  • possibilité laissée au système d'exploitation d'utiliser la RAM ou non si les ressources mémoires deviennent tendues.