Système d'Exploitation - Travaux Dirigés n°8

«Gestion des IPC dans UNIX : les segments de mémoire partagée»

Notions vues dans ce TD : les segments de mémoire partagée, les signaux.

Prochain TD : C'est le dernier ! ....

PS : Les parties correspondant à du travail à faire sont toutes en italiques ; le restant étant du complément au cours.


  1. Rappel sur les IPCs dans UNIX

    Les IPCs sont des outils de communication et de synchronisation entre des processus qui s'exécute sur une même machine. Les sémaphores qui ont été vu dans le TD précédent, permettent de gérer des mécanismes d'exclusion mutuelle (section critique) sur du code entre différents processus. Les files de message, dont un exemple complet (code) a été donné en cours, permettent une communication asynchrone par échange de messages entre des processus ; elles permettent également le multiplexage de messages entre différents processus lecteurs. Enfin les segments de mémoire partagée, qui font l'objet de ce TD, permettent le partage d'une zone de la mémoire centrale par différents processus, chacun voyant cette zone comme une extension de son espace d'adressage.

    Ces objets sont indépendants du système de fichiers d'Unix et de la gestion des processus. A chaque type d'objet correspond en mémoire une table consultable (commande "ipcs") depuis n'importe quel processus. La manipulation de ces objets partagés nécessite un système de nommage indépendant de celui des fichiers (i-noeud) et des processus (pid). Pour accéder à un objet IPC l'un des processus concernés devra créer cet objet (fonction "shmget") en lui attribuant un nom (clé externe) grâce à la fonction "ftok". Pour construire ce nom cette méthode prend deux chaînes de caractères en paramètre, la première étant issus du système de fichier (par exemple "/tmp") alors que la deuxième sera quelconque. Les processus désirant accéder à cet objet IPC venant d'être créé, devront fabriquer cette même clé externe en appelant la méthode "ftok" avec les deux mêmes chaînes que celles utilisées par le premier processus (celui qui a créé l'objet).

    Après avoir créer un segment de mémoire, le processus courant doit rattacher ce dernier à son propre espace d'adressage virtuel (fonction "shmat"), soit en précisant l'adresse de rattachement, soit en laissant le système trouver cette adresse. Cette dernière méthode conseillée (la plus sûre) sera appliquée en prenant NULL (cf. l'exemple donné) comme deuxième paramètre de l'appel à "shmat". Tous les autres processus voulant partager ce segment devront aussi effectuer cette opération de rattachement. Cette fonction de rattachement "shmat" renvoie un pointeur (*) sur n'importe quel type de donnée (void). Cela dépendra de ce que l'on a mis dans le segment.

    Lorsqu'un processus n'a plus besoin d'utiliser un segment de mémoire il peut le détacher de son espace d'adressage virtuel (fonction "shmdt" avec l'adresse du segment en paramètre).

    Enfin lorsqu'un segment n'est plus utilisé par les processus concernés, il peut être détruit soit par le dernier processus qui l'utilise (fonction "shmctl"), soit à partir d'un shell (commande "ipcrm [ shm | msg | sem ] id").

    key_t cle ;

    int taille ; // taille du segment de mémoire créé

    struct donnees * commun ; // structure définissant le type des données manipulées dans le segment partagé

    ...

    cle = ftok ("/tmp", 'A') ; // création de la clé externe - nom partageable par les processus

    taille = sizeof (struct donnees) ;

    id = shmget ( cle , taille, IPC_CREAT | IPC_EXCL | 0666 ) ; // création de l'objet lui-même

    // attachement en lecture/écriture du segment au processus courant à l'adresse définie par "commun"

    commun = (struc donnees *) shmat (id, NULL, SHM_R | SHM_W ) ;

    ...

    shmdt ( (char *) commun ) ; // détachement du segment de l'espace d'adressage virtuel du processus courant

    shmctl (id, IPC_RMID, NULL) ; // destruction de l'objet IPC d'identité "id"


Problème

    Ecrire deux programmes indépendants "cons.c" et "prod.c" qui communiqueront via un segment de mémoire partagée. Ils seront lancés simultanément dans deux shell différents.

    while (1)

    {

    if (scanf (" %d ", & reponse) != 1 ) break ; // si le scanf n'a pas marché correctement il retourne une valeur autre que "1".

    }

    Après réception du signal (Ctrl C) ou lecture d'un caractère alphabétique et avant de se terminer, le processus devra détacher le segment de mémoire de son espace d'adressage virtuel et détruire le segment de mémoire.