System V Shared Memory sous Linux

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:

System V Shared Memory sous Linux

Post by Hydraxx »

System V Shared Memory sous Linux

Objectif

La mémoire partagée System V permet à plusieurs processus d'accéder aux mêmes données sans recopier les données via un pipe ou une file de messages à chaque échange.

C'est un mécanisme d'IPC très rapide.

1. Principe

Code: Select all

Processus A                    Processus B

adresse virtuelle A           adresse virtuelle B
        |                              |
        +------------+-----------------+
                     |
                     v
          mêmes pages mémoire
Les processus peuvent avoir des adresses virtuelles différentes tout en accédant au même contenu partagé.

Même mémoire partagée ne signifie pas même adresse virtuelle.

2. En-têtes

Code: Select all

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
3. Cycle de vie

Code: Select all

shmget()
   |
   v
shmid
   |
   v
shmat()
   |
   v
adresse virtuelle
   |
   v
lecture / écriture
   |
   v
shmdt()
   |
   v
shmctl(... IPC_RMID ...)
4. Création / ouverture : shmget()

Prototype :

Code: Select all

int shmget(key_t key,
           size_t size,
           int shmflg);
Exemple :

Code: Select all

key_t key = ftok("/tmp/shm.key", 'S');

int shmid = shmget(key,
                   4096,
                   IPC_CREAT | 0600);

if (shmid == -1) {
    perror("shmget");
}
5. Taille

size représente la taille demandée du segment.

Le kernel travaille en pages mémoire et la taille réelle allouée est gérée conformément aux contraintes du système.

Pour ouvrir un segment existant, la taille demandée doit respecter les règles de l'API.

6. IPC_CREAT et IPC_EXCL

Création si nécessaire :

Code: Select all

shmget(key, 4096, IPC_CREAT | 0600);
Création stricte :

Code: Select all

shmget(key,
       4096,
       IPC_CREAT | IPC_EXCL | 0600);
Si le segment existe déjà, IPC_EXCL provoque EEXIST.

7. Huge pages : SHM_HUGETLB

Linux propose des extensions comme :

Code: Select all

SHM_HUGETLB
pour demander l'utilisation de huge pages.

C'est une fonctionnalité avancée, soumise aux capacités et à la configuration du système.

Elle n'est pas nécessaire pour utiliser la mémoire partagée classique.

8. Attacher : shmat()

Prototype :

Code: Select all

void *shmat(int shmid,
            const void *shmaddr,
            int shmflg);
Exemple recommandé :

Code: Select all

void *ptr = shmat(shmid, NULL, 0);

if (ptr == (void *)-1) {
    perror("shmat");
}
Avec shmaddr = NULL, le kernel choisit l'adresse virtuelle.

C'est généralement le comportement à préférer.

9. Valeur de retour de shmat()

En cas de succès :

Code: Select all

adresse virtuelle du segment
En cas d'erreur :

Code: Select all

(void *) -1
Ne pas tester shmat() contre NULL pour détecter l'erreur.

Test correct :

Code: Select all

if (ptr == (void *)-1)
10. Attachement en lecture seule

Flag :

Code: Select all

SHM_RDONLY
Exemple :

Code: Select all

void *ptr = shmat(shmid,
                  NULL,
                  SHM_RDONLY);
Le processus peut alors lire le segment mais pas le modifier.

11. SHM_RND

SHM_RND intervient lorsque le programme demande une adresse d'attachement particulière.

Il permet d'arrondir l'adresse selon les contraintes définies par le système.

Dans la majorité des programmes :

Code: Select all

shmaddr = NULL
reste plus simple et plus portable.

12. Lire et écrire

Après shmat(), la zone se manipule comme de la mémoire normale.

Exemple :

Code: Select all

char *mem = shmat(shmid, NULL, 0);

strcpy(mem, "bonjour");
Autre processus :

Code: Select all

char *mem = shmat(shmid, NULL, SHM_RDONLY);

printf("%s\n", mem);
13. Partager une structure

Exemple :

Code: Select all

struct shared_data {
    int counter;
    char text[256];
};
Puis :

Code: Select all

struct shared_data *data;

data = shmat(shmid, NULL, 0);

data->counter++;
strcpy(data->text, "test");
14. Synchronisation obligatoire

La mémoire partagée ne fournit pas de synchronisation automatique.

Exemple dangereux :

Code: Select all

Processus A : counter++
Processus B : counter++
Les opérations peuvent entrer en compétition.

Résultat possible :

Code: Select all

race condition
Il faut utiliser un mécanisme de synchronisation :
  • System V semaphore ;
  • POSIX semaphore ;
  • mutex process-shared ;
  • autre protocole adapté.
15. Combinaison avec un sémaphore

Code: Select all

lock semaphore

modifier shared memory

unlock semaphore
Exemple conceptuel :

Code: Select all

reserveSem(semid);

data->counter++;

releaseSem(semid);
16. Détacher : shmdt()

Prototype :

Code: Select all

int shmdt(const void *shmaddr);
Exemple :

Code: Select all

if (shmdt(ptr) == -1) {
    perror("shmdt");
}
shmdt() retire le mapping du processus courant.

shmdt() ne signifie pas forcément supprimer le segment System V.

17. Suppression : shmctl()

Prototype :

Code: Select all

int shmctl(int shmid,
           int cmd,
           struct shmid_ds *buf);
Commande importante :

Code: Select all

IPC_RMID
Exemple :

Code: Select all

if (shmctl(shmid, IPC_RMID, NULL) == -1) {
    perror("shmctl IPC_RMID");
}
18. Suppression différée

Lorsqu'un segment encore attaché est marqué avec IPC_RMID, sa destruction complète est liée à la fin des attachements restant selon les règles du système.

Conceptuellement :

Code: Select all

IPC_RMID
   |
   v
segment marqué pour suppression
   |
   v
dernier processus se détache
   |
   v
destruction finale
19. IPC_STAT

Exemple :

Code: Select all

struct shmid_ds ds;

if (shmctl(shmid, IPC_STAT, &ds) == -1) {
    perror("shmctl IPC_STAT");
}
Cette structure contient notamment :
  • permissions ;
  • taille du segment ;
  • PID du créateur ;
  • PID de la dernière opération ;
  • nombre de processus attachés ;
  • dates importantes.
20. IPC_SET

IPC_SET permet de modifier certains champs autorisés de la structure de contrôle.

Cela concerne principalement des métadonnées administratives et permissions.

21. Adresses virtuelles différentes

Exemple :

Code: Select all

Processus A :
base = 0x7f100000

Processus B :
base = 0x6a300000
Les deux peuvent pourtant accéder aux mêmes données physiques.

Conséquence :

un pointeur absolu stocké dans le segment peut être invalide dans un autre processus.

22. Mauvais exemple : pointeur absolu

Code: Select all

struct data {
    char *ptr;
};
Si le processus A écrit :

Code: Select all

ptr = 0x7f100100
cette adresse peut n'avoir aucun sens dans le processus B.

23. Bonne solution : offsets

Stocker un déplacement depuis le début du segment :

Code: Select all

struct data {
    size_t offset;
};
Reconstruction :

Code: Select all

char *base = shared_base;

char *ptr = base + data->offset;
Chaque processus utilise sa propre adresse de base.

24. Structures contenant des pointeurs

Règle pratique :

Dans une structure réellement partagée entre processus, préférer :
  • offsets ;
  • indices ;
  • tailles ;
  • identifiants ;

plutôt que des pointeurs virtuels absolus.


25. Emplacement dans l'espace virtuel

Après shmat(), le segment apparaît comme une région du processus.

Sous Linux, on peut inspecter :

Code: Select all

/proc/<pid>/maps
et observer la région correspondant à la mémoire partagée.

26. Comparaison avec mmap()

System V shared memory :

Code: Select all

shmget()
shmat()
shmdt()
shmctl()
Memory mapping moderne :

Code: Select all

mmap()
munmap()
Les deux permettent de mapper des pages dans l'espace virtuel, mais avec des API, modèles et origines historiques différentes.

27. Verrouillage mémoire

Certaines commandes ou extensions permettent de verrouiller des pages en mémoire afin d'éviter qu'elles soient évincées.

Ce sont des fonctions avancées qui dépendent des permissions et de la configuration du système.

Elles ne sont pas nécessaires pour la majorité des usages de shared memory.

28. Exemple simple : producteur / consommateur

Structure :

Code: Select all

struct shared_data {
    int ready;
    char buffer[1024];
};
Producteur :

Code: Select all

lock();

strcpy(shared->buffer, "message");
shared->ready = 1;

unlock();
Consommateur :

Code: Select all

lock();

if (shared->ready) {
    printf("%s\n", shared->buffer);
    shared->ready = 0;
}

unlock();
Le vrai protocole doit gérer correctement l'attente et l'ordre des opérations.

29. Exemple de création

Code: Select all

key_t key = ftok("/tmp/shm.key", 'S');

if (key == (key_t)-1) {
    perror("ftok");
    exit(EXIT_FAILURE);
}

int shmid = shmget(key,
                   sizeof(struct shared_data),
                   IPC_CREAT | 0600);

if (shmid == -1) {
    perror("shmget");
    exit(EXIT_FAILURE);
}
30. Exemple d'attachement

Code: Select all

struct shared_data *shared;

shared = shmat(shmid, NULL, 0);

if (shared == (void *)-1) {
    perror("shmat");
    exit(EXIT_FAILURE);
}
31. Exemple de détachement

Code: Select all

if (shmdt(shared) == -1) {
    perror("shmdt");
}
32. Exemple de suppression

Code: Select all

if (shmctl(shmid, IPC_RMID, NULL) == -1) {
    perror("shmctl");
}
33. Limites kernel

Le système impose diverses limites :
  • nombre de segments ;
  • taille minimale ;
  • taille maximale ;
  • quantité totale de mémoire partagée ;
  • nombre de segments attachés selon l'implémentation.
Noms classiques :

Code: Select all

SHMMNI
SHMMIN
SHMMAX
SHMALL
Les valeurs exactes ne sont pas à mémoriser.

34. Inspection

Afficher les segments :

Code: Select all

ipcs -m
Informations Linux :

Code: Select all

cat /proc/sysvipc/shm
Supprimer manuellement :

Code: Select all

ipcrm -m <shmid>
35. Erreurs fréquentes
  • EEXIST : segment déjà présent avec IPC_EXCL.
  • EACCES : permissions insuffisantes.
  • EINVAL : taille, identifiant ou paramètres invalides.
  • ENOMEM : mémoire / espace virtuel insuffisant.
  • EIDRM : segment marqué/supprimé dans certains scénarios.
36. Avantages
  • très rapide ;
  • pas de copie de message via le kernel à chaque échange ;
  • adaptée aux gros volumes de données ;
  • accès naturel sous forme de mémoire.
37. Inconvénients
  • synchronisation à gérer séparément ;
  • risques de race conditions ;
  • pointeurs absolus problématiques ;
  • gestion du cycle de vie plus complexe ;
  • objets System V persistants ;
  • API historique.
38. API à retenir

Code: Select all

shmget()   // créer / ouvrir un segment
shmat()    // attacher dans l'espace virtuel
shmdt()    // détacher
shmctl()   // contrôler / supprimer
39. Carte mentale

Code: Select all

key
 |
 v
shmget()
 |
 v
shmid
 |
 v
shmat()
 |
 v
adresse virtuelle
 |
 +-- lire
 +-- écrire
 |
 v
shmdt()

administration :
shmctl()
   |
   +-- IPC_STAT
   +-- IPC_SET
   +-- IPC_RMID
40. Points à retenir absolument
  • plusieurs processus voient les mêmes données.
  • les adresses virtuelles peuvent être différentes.
  • shmat() retourne (void *)-1 en cas d'erreur.
  • shared memory ne synchronise rien.
  • protéger les données avec un mécanisme adapté.
  • ne pas stocker naïvement des pointeurs absolus.
  • préférer offsets et indices.
  • shmdt() détache.
  • IPC_RMID supprime l'objet.
  • shmget / shmat / shmdt / shmctl constituent le cœur de l'API.

Who is online

Users browsing this forum: No registered users and 0 guests