IPC système V - La mémoire partagée

 

 

Un des trois dispositifs IPC est la mémoire partagée: Ce dispositif permet l'échange de données entre plusieurs processus. Pour y parvenir, ces données seront placées dans un segment de mémoire que ces différents processus connaîtront. Ce dispositif permet ainsi de réduire les appels au noyau (par opposition au PIPE ou tube ou encore aux files de messages).

Cependant, pour une bonne gestion de la mémoire partagée, celle-ci devra être couplée à un dispositif de verrouillage par sémaphore pour éviter les conflits d’accès. En effet, il faudra qu'un processus détermine s'il peut ou non accéder à la mémoire partagée avant de faire une mise à jour.

 

Informations liés à un segment de mémoire partagée

- Pour pouvoir utiliser les fonctions dont les explications vont suivre, il faut déclarer les fichiers de définition suivant:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

 

Création et ouverture d’un segment de mémoire partagée

La fonction shmget() retourne un identifiant permettant de manipuler le segment ouvert ou créé.

int shmget (key_t key, int size , int shmflag);

En entrée:

key est la clef IPC obtenue avec ftok() (voir obtention d'une clé unique), et qui caractérise l'identifiant du segment. Vous passez par une clef unique d’identification, dans ce cas, la mémoire partagée ne sera créée que si IPC_CREAT est positionné dans mode et qu'il n'existe déjà pas une file de message pour cette clef. Ou par IPC_PRIVATE qui garantie la création d'un canal IPC avec une key unique.
size indique la taille du segment en octets.
shmflag indique les permissions et le mode d'ouverture: 0666 (en octal) avec IPC_CREAT et/ou IPC_EXCL (utilisez le | pour les combiner: 0666 | IPC_CREAT | IPC_EXCL.

En sortie:

Un retour -1 signale une erreur de type errno. Sinon, il s'agit d'un identifiant shmid de type int

Image non trouvée !shmget permet d'ouvrir ou de créer un segment de mémoire partagée, mais ne permet pas d'y accéder physiquement. Il faudra utiliser la fonction char *shmat( int shmid , char *shmaddr , int shmflag) pour avoir un pointeur sur cette zone mémoire (expliqué ci-dessous) !

Codes erreurs dans errno:

Code Désignation
EINVAL Taille inférieur à SHMMIN ou supérieur à SHMMAX.
EEXIST IPC_CREAT | IPC_EXCL Or, le segment existe déjà.
EIDRM Le segment est détruit.
ENOSPC Plus d'identifiant disponible ou l'allocation d'un segment partagé de taille size dépasserait les limites de mémoire partagée du système.
ENOENT Aucun segment n'est associé à clé, et IPC_CREAT n'etait pas indiqué.
EACCES Autorisations insuffisantes pour accéder au segment
ENOMEM Plus de mémoire disponible pour le système.

 


Attacher un segment de mémoire partagée

La fonction shmat() permet d'attacher le segment au processus courant:

char *shmat( int shmid , char *shmaddr , int shmflag);

En entrée:

shmid est l'identifiant du segment retourné par shmget().
shmaddr précise l'adresse du segment attaché, en pratique il doit être placé à NULL, dans ce cas le noyau allouera une adresse.
shmflag indique les conditions d'accès au segment, par défaut 0 autorise la lecture et l'écriture, SHM_RDONLY interdit l'écriture.

La fonction shmat retourne l'adresse du segment attaché en cas de reussite, - 1 sinon et le code erreur est dans errno.

Image non trouvée !Comme souvent sur les systèmes, suite à l'allocation mémoire et la récupération d'un pointeur, veuillez à vider la zone allouée à votre segment, celle-ci peut contenir des données aléatoires...

Codes erreurs dans errno:

Code Désignation
EACCES Permission refusée pour l'attachement.
EINVAL shmid est invalide, shmaddr est mal alignée ou l'attachement a échoué sur brk.
ENOMEM Pas assez de mémoire pour le système.

 

 

Détacher un segment de mémoire partagée

La fonction shmdt() permet de détacher un segment de mémoire du processus courant:

int shmdt (char *shmaddr);

En entrée:

shmaddr est l'adresse du segment à détacher.

En sortie

La fonction shmdt retourne 0 ou -1 en cas d'erreur et errno vaut EINVAL pour indiquer une adresse invalide.

Image non trouvée !Cette fonction ne supprime pas un segment de mémoire partagée, elle se contente simplement de détacher le segment du processus exécutant le shmdt.

Pour effacer le segment, voir la fonction suivante shmctl

 

Contrôle, maj, suppression, vérrouillage/dévérouillage de la mémoire partagée

La fonction shmctl va permettre de récupérer et manipuler les informations liées à la mémoire partagée via son identifiant shmid

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

shmid est l'identifiant du segment retourné par shmget().
cmd est l'une des commandes précisées ci-dessous.
buf est un pointeur sur une structure shmid_ds.

La valeur de cmd peut-etre l'une des suivantes:

IPC_STAT Permet de lire la structure shmid_ds.
IPC_SET Permet de modifier la structure shmid_ds.
IPC_RMID Permet de supprimer le segment de mémoire partagée, buf pourra être positionné à NULL dans ce cas. La suppression sera effective lorsque le membre shm_nattch sera passé à zéro. Seuls les créateur/propriétaire ou le super utilisateur peuvent lancer cette fonction.

Image non trouvée !Si le segment mémoire contient des données confidentielles, veuillez bien à effacer le contenu de cette zone avant de supprimer le segment ! En effet, lors de la prochaine allocation mémoire, une partie des données du segment mémoire détruit pourrait devenir accessible à d'autres programmes !

Sous Linux pour éviter ou autoriser le swapping:
SHM_LOCK Vérouillage en mémoire du segment (SU seulement).
SHM_UNLOCK Dévérouillage du segment (SU seulement).

La fonction shmctl() retourne 0 si pas d'erreurs, -1 sinon.

Une structure shmid_ds est utilisée pour chaque mémoire partagée.

Principaux codes retours possibles dans errno:

Code Désignation
EACCES Suite IPC_STAT mais shm_perm.modes ne permetant pas la lecture du segment shmid.
EFAULT cmd a la valeur IPC_SET ou IPC_STAT mais buf pointe en-dehors de l'espace d'adressage accessible.
EINVAL shmid n'est pas un identificateur de segment valide, ou cmd n'est pas une commande reconnue.
EIDRM shmid pointe sur un segment détruit.
EPERM Sur IPC_SET ou IPC_RMID Permission refusé car le processus n'est ni le propriétaire du segment, ni son créateur, ni le Super-Utilisateur.

 

Les limites de la mémoire partagée

shmmax taille maxi du segment
shmin taille mini du segment
shmni nbr. maxi de segments
shmseg place restant sur la heap

Ces informations diffèrent suivant les systèmes, et les valeurs que j'avais pour quelques systèmes ne sont certainement plus valables aujourd'hui...

 

Exemple de code

Voici un programme très simple pour échanger de manière encore plus simple des données entre deux processus.

Le premier programme va créer un segment mémoire et y stocker un message. Puis il va attendre qu'une autre instance du même programme arrive et modifie ce message. Tous deux vont afficher les messages reçus. Les deux programmes rendent enfin la main après avoir détachés le segment. A noter que le programme ayant créé le segement mémoire va détruire le segment mémoire alloué !

Il n'y a pas d'utilisation de sémaphores, le code restant volontairement basic sur ce point. La clef unique utilisée est basée sur le nom du programme exécutable.

Image non trouvée !

 

Annexes

cf. administration IPC en Shell