Coopération entre processus

Partie I : Partage des ressources

© D. Mathieu    mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique
Créé le 06/10/2000 - Dernière mise à jour : 17/10/2001

Remarques préliminaires :

Sommaire

Outils nécessaires à la coopération entre processus
Classe d'exceptions spécifique aux IPCs
Classe COperation
Wrappers
Wrappers de l'I.P.C. "Ensemble de sémaphores"
Fonction semget()
Fonction semctl()
Fonctions spécifiques aux sémaphores : semop()
Classe CSemBase
Bibliothèque de fichiers objets
Modifications de MakeUtil
Modifications de Makefile
Partage de ressources
Section critique  exo_01
Primitive P() non bloquante  exo_02
Simulation du contrôle d'accès à une piscine  exo_03
Simulation du problème des cinq philosophes  exo_04


Outils nécessaires à la coopération entre processus

Classe d'exceptions spécifique aux IPCs

    Les fonctions de création des I.P.C.s (Inter Process Communication), de la famille xxxget(), ont pour premier paramètre une "clé", de type key_t, qui identifie l'I.P.C. à l'extérieur du programme, analogue aux "noms de fichiers" des fonctions de création des fichiers (opent(), creat()).

    Toutes les fonctions système qui manipulent les I.P.C.s (Inter Process Communication) ont pour premier paramètre un identificateur d'IPC :

de type int, qui joue le même rôle que le file descriptor des fichiers.

    Dans les fichiers CExcFctSyst.h et CExcFctSyst.hxx dériver la nouvelle classe CExcFctSystIPC de CExcFctSyst, analogue à CExcFctSystFile, en remplaçant le nom de fichier et le file descriptor par la clé de l'I.P.C. (m_key) et son identificateur (m_id).

Rq1

Corrigé : CExcFctSyst.h    -    CExcFctSyst.hxx

 Sommaire


Classe COperation

    Afin de pouvoir réaliser un ensemble d'opérations sur un ensemble de sémaphores de façon atomique, au moyen de la fonction semop(), il est nécessaire de fournir à cette dernière un vecteur d'opérations à effectuer, chacune étant une structure struct sembuf formée de trois champs :     Afin de faciliter le développement, la classe COperation, de l'espace de noms nsSysteme, vous est fournie dans les fichiers COperation.h et COperation.hxx du répertoire des corrigés tppartagersc/include. Recopiez-les dans vos répertoires correspondants étudiez sa déclaration afin de pouvoir l'utiliser ultérieurement.

Wrappers

    Plusieurs des fonctions système qui seront utilisées au cours des exercices suivants peuvent être appelées avec une grande variété de paramètres, la valeur de l'un modifiant la signification de l'autre (et souvent son type). Les profils réels de ces fonctions incluent donc le symbole ... (ellipse) déjà rencontré pour la fonction fcntl() par exemple. La possibilité de surcharge offerte par le C++ nous permettra de déclarer des wrappers ayant des profils différents selon les valeurs des paramètres. La seule contrainte est que deux wrappers différents n'aient pas le même profil.

    Cela conduit à écrire plus de 20 wrappers différents pour seulement quelques fonctions système de base, travail indispensable pour faciliter une programmation ultérieure sécurisée, mais gros consommanteur de temps. Nous supposons que le principe d'écriture en C++ de plusieurs wrappers d'une même fonction système est maintenant connu, les corrigés vous sont donc fournis. Cependant, pour pouvoir les utiliser correctement par la suite, vous devez en prendre connaissance de façon attentive avant de les intégrer dans les fichiers nsSysteme.hnsSysteme.hxx et nsSysteme.cxx. C'est pourquoi nous vous recommandons de suivre la procédure ci-dessous et de ne pas récupérer directement les versions finales des corrigés des fichiers nsSysteme.h et nsSysteme.hxx, accessibles ci-dessous.

    Bien que les fonctions système de la même famille (par exemple xxxget()) aient des profils très semblables, nous ne développerons pas leurs wrappers simultanément. Au contraire, nous introduirons ceux-ci au fur et à mesure de la nécessité de chaque type d' I.P.C.s. Ainsi, les wrappers s'appliquant aux familles de sémaphores seront développées dans ce TP ("Partage de ressources"), ceux qui concernent les files de messages seront introduits dans le TP consacré à l'implémentation du modèle "Producteurs/Consommateurs", ceux qui concernent les mémoires partagées seront introduits dans le TP consacré à l'implémentation du modèle "Lecteurs/Rédacteurs". Dans ces différents TPs seront introduits des wrappers d'autres fonctions système lorsqu'elles devront être utilisées.

Wrappers de l'I.P.C. "Ensemble de sémaphores"

Fonction semget()

    C'est à la famille xxxget() qu'appartient la fonction système semget(). Deux wrappers sont nécessaires :     Le nouveau type key_t utilisé dans les différentes fonctions est déclaré dans <sys/types.h>, alors que les profils des fonctions système, ainsi que les types sembuf et semid_ds sont décrits dans <sys/sem.h>.

    A ajouter au fichier nsSysteme.h :
 
    // Fonctions concernant les ensembles de sémaphores
    // ================================================

    int  Semget (::key_t key, int nsems, int semflg)
                      throw (CExcFctSystIPC);

    int  Semget (::key_t key)
                      throw (nsUtil::CExcFct);

    A ajouter au fichier nsSysteme.hxx (Rq2) :
 
//=================================================
// Fonctions concernant les ensembles de sémaphores
//=================================================

inline int nsSysteme::Semget (::key_t key, int nsems, int semflg)
    throw (CExcFctSystIPC)
{
    int SemId;
    if (-1 == (SemId = ::semget (key, nsems, semflg)))
        throw CExcFctSystIPC ("semget() : pour création de sem", key, -1);
    return SemId;

} // Semget()

    A ajouter au fichier nsSysteme.cxx :
 
//=================================================
// Fonctions concernant les ensembles de sémaphores
//=================================================

int nsSysteme::Semget (::key_t key) throw (CExcFct)
{
    if (IPC_PRIVATE == key)
        throw CExcFct ("semget() : clé IPC_PRIVATE sans droits",
                       CstSemgetErrArg);

    int SemId;
    if (-1 == (SemId = ::semget (key, 0, 0)))
        throw CExcFctSystIPC ("semget() : pour sem existant", key, -1);
    return SemId;

} // Semget()

    La constante CstSemgetErrArg = 133 doit être ajoutée au fichier CstCodErr.h.

Fonction semctl()

    C'est à la famille xxxctl() qu'appartient la fonction système semctl(). Plusieurs wrappers de cette fonction doivent être écrits, dont certains lèvent une exception CExcFct avec la constante CstSemctlErrArg lorsque les valeurs des paramètres sont incohérents :     La constante CstSemctlErrArg (= 134) doit être ajoutée dans le fichier CstCodErr.h.

    A ajouter au fichier nsSysteme.h :
 
    // uniquement pour détruire l'ensemble de sémaphores

    void Semctl (int semid)
                      throw (CExcFctSystIPC);

    // si cmd == GETVAL ou GETPID ou GETNCNT ou GETZCNT :

    int   Semctl (int semid, int semnum, int cmd)
                      throw (nsUtil::CExcFct);

    // si cmd == SETVAL :

    void Semctl (int semid, int semnum, int cmd, int val)
                      throw (nsUtil::CExcFct);

    // si cmd == IPC_STAT ou IPC_SET :

    void Semctl (int semid, int cmd, ::semid_ds * buf)
                      throw (nsUtil::CExcFct);

    // si cmd == GETALL ou SETALL

    void Semctl (int semid, int cmd, unsigned short array [])
                      throw (nsUtil::CExcFct);

    A ajouter au fichier nsSysteme.hxx (Rq3) :
 
#if defined (__GNU_LIBRARY__) && !defined (_SEM_SEMUN_UNDEFINED)
    /* union is defined by including <sys/sem.h> */
#else
    /* according to X/OPEN we have to define it ourselves */
    union semun
    {
        int val;                       //    <= value for SETVAL
        struct ::semid_ds *buf;        //    <= buffer for IPC_STAT & IPC_SET
        unsigned short int *array;     //    <= array for GETALL & SETALL
        struct ::seminfo *__buf;       //    <= buffer for IPC_INFO
    }; // semun

#endif     __GNU_LIBRARY__ & !_SEM_SEMUN_UNDEFINED

#define semun semun

// uniquement pour détruire l'ensemble de sémaphores

inline void nsSysteme::Semctl (int semid) throw (CExcFctSystIPC)
{
    ::semun Union;
    if (::semctl (semid, 0, IPC_RMID, Union))
        throw CExcFctSystIPC ("semctl() : destruction", -1, semid);

} // Semctl()

    A ajouter au fichier nsSysteme.cxx :
 

#ifndef semun
#if defined (__GNU_LIBRARY__) && !defined (_SEM_SEMUN_UNDEFINED)
    /* union is defined by including <sys/sem.h> */
#else
    /* according to X/OPEN we have to define it ourselves */
    union semun
    {
        int val;                       //    <= value for SETVAL
        struct ::semid_ds *buf;        //    <= buffer for IPC_STAT & IPC_SET
        unsigned short int *array;     //    <= array for GETALL & SETALL
        struct ::seminfo *__buf;       //    <= buffer for IPC_INFO
    }; // semun

#endif     __GNU_LIBRARY__ & !_SEM_SEMUN_UNDEFINED
#endif     semun

// si cmd == GETVAL ou GETPID ou GETNCNT ou GETZCNT :

int nsSysteme::Semctl (int semid, int semnum, int cmd) throw (nsUtil::CExcFct)
{
    if ((GETVAL != cmd) && (GETPID != cmd) && (GETNCNT != cmd) &&
        (GETZCNT != cmd))
        throw CExcFct ("semctl() : GETxxx", CstSemctlErrArg);

    int Val;
    ::semun Union;
    if (-1 == (Val = ::semctl (semid, semnum, cmd, Union)))
        throw CExcFctSystIPC ("semctl() : GETxxx", -1, semid);

    return Val;

} // Semctl()

// si cmd == SETVAL :

void nsSysteme::Semctl (int semid, int semnum, int cmd, int val)
    throw (nsUtil::CExcFct)
{
    if (SETVAL != cmd) throw CExcFct ("semctl() : SETVAL", CstSemctlErrArg);

    ::semun Union;
    Union.val = val;
    if (::semctl (semid, semnum, cmd, Union))
        throw CExcFctSystIPC ("semctl() : SETVAL", -1, semid);

} // Semctl()

// si cmd == IPC_STAT ou IPC_SET :

void nsSysteme::Semctl (int semid, int cmd, ::semid_ds * buf)
    throw (nsUtil::CExcFct)
{
    if ((IPC_STAT != cmd) && (IPC_SET != cmd))
        throw CExcFct ("semctl() : IPC_xxx", CstSemctlErrArg);

    ::semun Union;
    Union.buf = buf;
    if (::semctl (semid, 0, cmd, Union))
        throw CExcFctSystIPC ("semctl() : IPC_xxx", -1, semid);

} // Semctl()

// si cmd == GETALL ou SETALL

void nsSysteme::Semctl (int semid, int cmd, unsigned short array [])
    throw (nsUtil::CExcFct)
{
    if ((SETALL != cmd) && (GETALL != cmd))
        throw CExcFct ("semctl() : xxxALL", CstSemctlErrArg);

    ::semun Union;
    Union.array = array;
    if (::semctl (semid, 0, cmd, Union))
        throw CExcFctSystIPC ("semctl() : xxxALL", -1, semid);

} // Semctl()

Fonctions spécifiques

    La seule fonction système spécifique des ensembles de sémaphores est la fonction semop() :

    A ajouter au fichier nsSysteme.h :
 
    void Semop  (int semid, ::sembuf * sops, unsigned int nsops)
                      throw (CExcFctSystIPC);

    A ajouter au fichier nsSysteme.hxx :
 
inline void nsSysteme::Semop  (int semid, ::sembuf * sops,
                               unsigned int nsops) throw (CExcFctSystIPC)
{
    if (::semop (semid, sops, nsops))
        throw CExcFctSystIPC ("semop()", -1, semid);

} // Semop()

Corrigé : nsSysteme.h    -    nsSysteme.hxx    -    nsSysteme.cxx    -    CstCodErr.h

 Sommaire


Classe CSemBase

    Les ensembles de sémaphore d'Unix sont un outil de synchronisation très puissants, permettant de multiples utilisations. Cependant, ils sont un peu lourds à mettre en oeuvre pour des problèmes simples et il peut être agréable de disposer d'une classe de sémaphores binaires.

    A partir du répertoire des corrigés tppartagersc/include, recopier dans votre répertoire correspondant le fichier CSemBase.h. Il contient la déclaration d'une classe CSemBase, de l'espace de noms nsSysteme, permettant de créer un sémaphore n-aire (par défaut binaire). La donnée membre m_Proprietaire contient le pid du processus qui a créé le sémaphore par la fonction Semget() et qui est le seul habilité à le détruire par Semctl()). La donnée membre m_id contient l'identificateur de sémaphore renvoyé par Semget().

    Ecrire dans le fichier CSemBase.hxx le corps des fonctions membres de la classe.

    Modifier le fichier INCLUDE_H pour prendre en compte les fichiers inclus COperation.h, COperation.hxx, CSemBase.h, CSemBase.hxx. En profiter pour supprmier la macro COMPILER (explications plus loin).

Corrigé : INCLUDE_H    -    CSemBase.hxx

 Sommaire


Bibliothèque de fichiers objets

    Le nombre de fichiers communs dans le répertoire util devient important, ce qui, dans les fichiers Makefile, allonge la liste des dépendances de l'exécutable, comme par exemple :
 
$(nom) : cible $(nom).o 
        g++ -s -o $(nom) $(nom).o                                     \ 
                  $(UTIL)/CException.o    $(UTIL)/SaveSig.o           \ 
                  $(UTIL)/main.o          $(UTIL)/nsSysteme.o         \ 
                  $(UTIL)/TestFdOuverts.o $(UTIL)/Autopsie.o 

ainsi que la liste des fichiers .o à raffraîchir :
 
cible : 
        cd $(UTIL); make -f MakeUtil nsSysteme.o main.o nsUtil.o      \ 
                                     CException.o    SaveSig.o        \ 
                                     TestFdOuverts.o Autopsie.o 

    De plus, il devient facile de commettre des erreurs.

    Une bonne solution est de constituer une bibliothèque (library) de fichiers objets (.o), mise à jour à chaque recompilation, et d'indiquer au compilateur g++ d'aller chercher dedans les fichiers dont il peut avoir besoin. Cela implique la modification des fichiers MakeUtil et Makefile. Une bibliothèque doit commener par le préfixe lib, nous l'appellerons donc libSys.

Modifications de MakeUtil

    Toute compilation doit être suivie d'une mise à jour de la bibliothèque, au moyen de la commande Unix ar. La macro COMPILER du fichier MakeUtil doit donc être la suivante :
 
COMPILER = g++ -c -I$(INCLUDE) -Wall $*.cxx; ar -cqs libSys.a $*.o 

    De plus le nettoyage des fichiers compilés doit être modifié :
 
#
# Nettoyage du répertoire courant : bibliothèque et fichiers .o
#
clean :
        clear; rm -f *.o *.a -v

    Enfin, pour éviter de relancer toutes les compilations en énumérant chaque cible, il est préférable de les regrouper en une seule, placée en début de fichier :
 
general : nsUtil.o         CException.o   nsSysteme.o   main.o       \
         TimeBase.o        Time.o         CTimer.o      SaveSig.o    \
         TestFdOuverts.o   Autopsie.o

    Noter qu'il n'y a pas de commande associée. Modifications de Makefile     Recopier le fichier ../dirprocess/Makefile dans le répertoire de travail (dirpartagersc).

    Puisque la macro COMPILER a été supprimée du fichier INCLUDE_H, il est nécessaire de la réintroduire dans le fichier Makefile :
 
COMPILER = g++ -I$(INCLUDE) -c $*.cxx -Wall

    Dans l'exemple ci-dessous, nous supposons que le programme ne dépend pas de fichiers objets .o dans le répertoire courant.

    Pour l'édition de liens, il faut indiquer au compilateur g++, au moyen de l'option -L (assez semblable à l'option -I) qu'il doit aller chercher les références dans le répertoire util (-L$(UTIL)), dans la bibliothèque libSys, par l'option -l (-lSys).
 
$(nom) : cible $(nom).o
        g++ -s -o $(nom) $(nom).o                                      \
                  -L$(UTIL) -lSys

    Noter la suppression du préfixe lib.

    Il ne reste plus qu'à mettre à jour la cible :
 
cible :
        cd $(UTIL); make -f MakeUtil

 Sommaire


Partage de ressources

    Les exercices proposés illustrent l'utilisation de mutex, de sémaphores n-aires, puis de familles de sémaphores pour résoudre des problèmes de partage de ressources critiques de plus en plus complexes.
 

Section critique

exo_01
    Recopier les fichiers dirprocess/exo_06.cxx et dirprocess/exo_06.txt dans le fichier exo_01.cxx et exo_01.txt. Dans le père, créer un sémaphore binaire (mutex) dont la clé est passé en argument de la commande, puis mettre le corps du père et celui du fils dans deux sections critiques.

    Modifier le fichier Makefile, compiler et tester.

    Vous constatez que le programme ne fonctionne pas correctement. En effet, l'algorithme donne le droit de détruire l'ensemble de sémaphores seulement à son processus propriétaire (celui qui l'a créé). Il faut en réalité que ce soit le dernier processus à l'utiliser qui ait ce droit. Malheureusement, il est impossible de le connaître. Une solution simple, dans ce problème, consiste à obliger le processus créateur à être le dernier à se terminer : il suffit qu'il attende la mort de son fils.

    Faire la modification, compiler et tester.

    Cette fois, vous devez constater que deux seules séquences sont possibles : le père s'exécute complètement avant le fils, ou l'inverse. Cette solution est souvent utilisée, et le père peut être réduit à créer des ressources, puis lancer tous les processus qui doivent coopérer, et enfin attendre la fin de tous ces processus pour libérer les ressources (ici par simple appel au destructeur).

Corrigé : Makefile    -    exo_01.cxx

Remarque : que pensez-vous de la solution suivante ?
 
    char c;

    const int fd = Open ("exo_01.txt", O_RDWR);

    if (Fork ())                                        /* 01 */
    {
        CMutex Mutex (atoi (argv [1]));
        Mutex.P();
        Lseek (fd, 10, SEEK_SET);                       /* 02 */
        Read  (fd, &c, 1);                              /* 03 */
        cout << "lu par le pere : " << c << endl;       /* 04 */
        Read  (fd, &c, 1);                              /* 05 */
        cout << "lu par le pere : " << c << endl;       /* 06 */
        Mutex.V();
        Wait ();
    }
    else
    {
        CMutex Mutex (atoi (argv [1]));
        Mutex.P();
        Lseek (fd, 3, SEEK_SET);                        /* 07 */
        Write (fd, "ABCDE", 3);                         /* 09 */
        cout << "lu par le fils : " << c << endl;       /* 10 */
        Mutex.V();
    }
 

    Vous pouvez l'essayer, il s'agit du fichier exo_01b.cxx dans le répertoire des corrigés dirpartagersc

Corrigé

 Sommaire


Primitive P() non bloquante

exo_02
    Nous avons vu deux méthodes permettant à un processus d'attendre l'occurrence d'un événement :     Cet exercice a pour objectif de mettre en place une structure que l'on rencontre fréquemment en informatique, qui consiste à répéter une tentative d'opération un "certain" nombre de fois, jusqu'à satisfaction ou jusqu'à atteindre un nombre d'essais maximal. L'événement attendu ici est la libération d'une ressource.

    Plusieurs processus nécessitent l'écran pour leurs affichages mais, afin de ne pas mélanger les éditions, son utilisation est mise en exclusion mutuelle au moyen d'un sémaphore binaire. Si après plusieurs tentatives infructueuses, séparées d'un délai d'attente variable, un processus ne peut accéder à l'écran, il finit par se décider à rediriger ses éditions sur un fichier. Pour réaliser cet algorithme, il faut modifier la primitive P(). En effet, cette dernière est bloquante et le processus ne peut pas "l'essayer" pour voir s'il peut accéder à la ressource. Il faut donc enrichir la classe CSemBase.

    Dans les fichiers include/CSemaphore.h, include/CSemaphore.hxx et util/CSemaphore.cxx, dériver la classe CSemBase en CSemaphore. Lui ajouter la fonction membre :
 
 bool P_WouldBlock (void);

qui ne bloque jamais le processus mais qui renvoie vrai chaque fois que le processus aurait dû être bloqué (utiliser le flag IPC_NOWAIT de la fonction semop()).

    Modifier les fichiers INCLUDE_H, Makefile et MakeUtil.

    Pour créer et détruire les ressources (un ensemble de sémaphores) nous utiliserons ici une nouvelle technique : dans le fichier exo_02d.cxx, écrire un programme qui :

    Dans le fichier exo_02.cxx, écrire un programme qui, à intervalles réguliers, essaie de s'accaparer l'écran en bloquant le sémaphore créé par exo_02d (utilisation de P_WouldBlock()). S'il y arrive, il affiche un certain nombre de fois son numéro de pid (une fois/seconde) puis libère l'écran. S'il n'y arrive pas, il effectue la même opération sur un fichier de secours. Les paramètres de la commande exo_02 sont :

    Compiler et tester en lançant simultanément (script [1] ?) plusieurs processus exo_02 (trois suffisent) en choisissant leurs arguments pour que l'un d'eux prenne l'écran, un autre finisse par écrire sur le fichier et le troisième patiente assez longtemps pour récupérer l'écran.

Corrigés : exo_02d.cxx    -    exo_02.cxx    -    CSemaphore.h    -    CSemaphore.hxx    -    CSemaphore.cxx    -    INCLUDE_H    -    Makefile    -    MakeUtil    -    ScriptEcran

 Sommaire


Simulation du contrôle d'accès à une piscine

exo_03

    Il est fastidieux de retrouver le pid du démon qui doit détruire les ressources lorsque tous les processus utilisateurs sont terminés, et de lui envoyer un signal particulier (passé en paramètre). Pour simplifier cette tâche, le démon génèrera un script, de nom prédéfini (ou passé en paramètre), qui effectuera ces opérations.

    Dans les fichiers Reveil.h et Reveil.cxx des répertoires include et util, déclarer et définir la fonction de profil :
 
void nsUtil::Reveil (int NumSig                    = SIGKILL,
                     const std::string & NomScript = "Reveil")
    throw (nsSysteme::CExcFctSystFile);

    La fonction doit générer un script (fichier exécutable) d'identificateur NomScript contenant :

    L'architecture générale du système à mettre en place est la suivante :     Les profils des fonctions du tableau 1 sont regroupées dans le fichier nsPiscine.h que vous pouvez récupérer dans le répertoire tppartagersc/dirpartagersc des corrigés.

    Dans le fichiers nsPiscine.cxx correspondant, étendre l'espace de noms nsPiscine pour lui ajouter :

namespace nsPiscine
{
    int SemId;
    enum { CstSemPanier = 0, CstSemCabine = 1, };

} // nsPiscine

    Ajouter au fichier les corps des différentes fonctions de synchronisation.

    Recopier le fichier exo_02d.cxx dans exo_03d.cxx. En utilisant les fonctions écrites ci-dessus, modifier le démon pour qu'il réalise les opérations suivantes :

    Dans le fichier Baigneur.cxx, et en utilisant les fonctions écrites ci-dessus, écrire un programme qui simule le comportement d'un baigneur : prendre un panier, occuper une cabine, se déshabiller, quitter la cabine et se baigner, entrer dans une cabine pour se rhabiller, puis sortir de la cabine et rendre le panier. Les arguments de la commande Baigneur sont :     Compléter le fichier Makefile pour qu'il puisse compiler les fichiers nsPiscine.cxx, exo_03d.cxx et Baigneur.cxx. Complér le fichier INCLUDE_H avec les dépendances de Reveil.h. Complér le fichier MakeUtil avec la compilation de Reveil.cxx.

    Recopier le fichier Chrono.cxx de votre répertoire dirprocess ou du répertoire tppartagersc des corrigés. Le compiler.

    Compiler, écrire le script ScriptPiscine pour lancer simultanément plusieurs baigneurs et le programme Chrono en tâche de fond, qui permettra de reconstituer le chronogramme. Tester.

Corrigé : Baigneur.cxx    -    exo_03d.cxx    -    ScriptPiscine    -    Reveil.h    -    Reveil.cxx    -    Makefile    -    MakeUtil    -    INCLUDE_H    -    nsPiscine.h    -    Chrono.cxx    -    nsPiscine.cxx

 Sommaire


Simulation du problème des cinq philosophes (facultatif)

    Cinq assiettes de spaghettis sont disposées sur une table ronde, une fourchette entre chaque paire d'assiettes. Chaque philosophe passe son temps à réfléchir puis à manger, puis réfléchir à nouveau, etc... Pour manger, il doit accéder simultanément aux deux fourchettes de part et d'autre de son assiette. Les ressources critiques sont ici les cinq fourchettes. Le philosophe i utilise les fourchettes i et i + 1 (modulo 5).

    Recopier le fichier exo_03d.cxx dans exo_04d.cxx. Modifier le démon pour qu'il crée les ressources nécessaires.

    Dans le fichier Philosophe.cxx, écrire un programme qui simule le comportement d'un philosophe. Les arguments des commandes Philosophe sont :

    Comme précédemment, on peut proposer un ensemble de fonctions, regroupées dans un espace de noms nsPhilosophe (fichiers nsPhilosophe.h et nsPhilosophe.cxx) permettant de modéliser le comportement des philosophes :
 
void DresserLaTable     (key_t Cle)   throw (nsSysteme::CExcFctSystIPC);
void DebarrasserLaTable (void)        throw (nsSysteme::CExcFctSystIPC);
void ArriverATable      (key_t Cle)   throw (nsSysteme::CExcFctSystIPC);
void DebutManger        (const int i) throw (nsSysteme::CExcFctSystIPC);
void FinManger          (const int i) throw (nsSysteme::CExcFctSystIPC);
void QuitterLaTable     (void)        throw (nsSysteme::CExcFctSystIPC);

    Compléter le fichier Makefile pour qu'il puisse compiler les fichiers nsPhilosophe.cxx, exo_04d.cxx et Philosophe.cxx. Il est inutile de modifier le fichier INCLUDE_H.

    Compiler et tester au moyen d'un script qui lance le programme Chrono en tâche de fond, puis les cinq philosophes.

Corrigé : nsPhilosophe.h    -    nsPhilosophe.cxx    -    Philosophe.cxx    -    ScriptPhilosophe    -    Makefile    -    exo_04d.cxx

 Sommaire


Téléchargement des corrigés :

tppartagersc.zip

Chemins d'accès aux sources des corrigés :

~mathieu/PARTAGE/src/tp/tpsys/tppartagersc/dirpartagersc
~mathieu/PARTAGE/src/tp/tpsys/tppartagersc/util
~mathieu/PARTAGE/src/tp/tpsys/tppartagersc/include


[1] Rappel : un script est un fichier texte qui doit être exécutable par le c-shell. Pour cela il doit :

© D. Mathieu    mathieu@romarin.univ-aix.fr
I.U.T.d'Aix en Provence - Département Informatique