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
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.
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.
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.
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.
Annexes
cf. administration IPC en Shell