Introduction 1 Préparation by JasonDetriou

VIEWS: 34 PAGES: 5

									Licence 3 Informatique 2007/2008                                        Semaines du 12 et 19 novembre 2007
Université de Provence


                                      Systèmes d'exploitation - TP4



                  OS/161 : synchronisation de threads
                    Benoît Masson, d'après Nicolas Ollinger et Matt Welsh


   Ce travail sera fait en   binômes, et devra être rendu. Il comptera pour une partie de la note de projet.


Introduction
Dans ce devoir, vous allez mettre en oeuvre des primitives de synchronisation dans le noyau d'OS/161.
Le devoir se décompose en deux parties : des questions pour lire le code, le comprendre ; puis une phase
de programmation, de modication d'OS/161 pour ajouter de nouvelles primitives. Les réponses aux
questions ainsi que le code produit seront rendus.
Pour aborder ce devoir dans de bonnes conditions, il est important d'avoir compris les chapitres de cours
sur l'ordonnancement et la synchronisation, et également le TP préliminaire sur OS/161. Dans la partie
programmation, il est important de bien organiser votre code, de le commenter et de produire quelque
chose de lisible, dans un style et des conventions proches du code que vous enrichissez.

1     Préparation
1.1 Conguration d'OS/161
OS/161 met à votre disposition un peu de code source pour faciliter la réalisation de ce TP à travers
un pilote (kern/asst1) et des entrées du menu au boot. Pour activer ce code, il faut recongurer votre
noyau, comme vous l'avez fait au TP précédent mais en utilisant le chier de conguration ASST1 au
lieu de ASST0 :
~$ cd $CS161/kern/conf
~/os161/kern/conf$ ./config ASST1


1.2 Compilation du noyau
Comme dans le TP précédent, vous compilerez le noyau avec la commande make mais en utilisant le
répertoire compile/ASST1 au lieu de compile/ASST0 :
~/os161/kern/conf$ cd ../compile/ASST1
~/os161/kern/compile/ASST1$ make depend
~/os161/kern/compile/ASST1$ make
~/os161/kern/compile/ASST1$ make install


1.3 Exécution du noyau
~/os161/kern/compile/ASST1$ cd $CS161/root
Pour exécuter les tests fournis vous aurez besoin de plus de mémoire dans l'émulateur System/161.
Modiez sys161.conf et modiez la ligne de busctl pour qu'elle ressemble à ce qui suit :
31 busctl ramsize=2097152


                                                        1
Vous pouvez alors utiliser l'émulateur comme précédemment :
~/os161/root$ sys161 kernel


1.4 Préparation de SVN
Avant de commencer à modier OS/161, enregistrons la version courante des sources pour pouvoir
facilement ensuite identier les modications apportées :
~$ cd $CS161/os161
~/os161/os161$ svn commit -m 'TP 4'
~/os161/os161$ svn update

À tout instant, pour connaître les chiers modiés, tapez svn status. Pour connaître les modications,
tapez svn diff. Pour plus d'informations, consultez la documentation de Subversion à l'adresse
                                     http://svnbook.red-bean.com/


2     Programmation concurrente
Un code correctement synchronisé ne doit pas être inuencé par l'ordre des changements de contexte.
Les tests peuvent acher les messages dans un ordre diérents mais satisfaire les contraintes imposées
par chaque test et surtout ne pas donner lieu à des interblocages (deadlocks ).

2.1 Tests
Les tests de threads fournis dans le menu d'OS/161 utilisent les sémaphores comme primitive de synchro-
nisation. Pour mieux comprendre comment fonctionne l'ordonnanceur d'OS/161, comment les threads
sont créés, comment se passe un changement de contexte, vous pouvez utiliser le débuggueur GDB, par
exemple tracer la fonction mi_switch().
Le test tt1 (thread test 1 ) ache les nombres de 0 à 7 en boucle dans chaque thread. Le test tt2 (thread
test 2 ) ne les ache qu'à l'entrée et à la sortie du thread.



2.2 Écriture de programmes concurrents
An de faciliter la mise au point de ce TP, les options passées au noyau assurent que les threads appellent
au démarrage thread_yield() aléatoirement. Pour identier et corriger des bugs, il peut être souhaitable
d'obtenir une suite reproductible de changements de contexte. Pour ce faire, il convient de xer la racine
du générateur de nombres pseudo-aléatoire utilisé. Pour cela, vous pouvez dans sys161.conf xer la
racine du générateur comme suit :
28 random seed=54
Ici on a choisi la racine 54. Bien sûr, vous pouvez remplacer 54 par la valeur de votre choix et la faire varier
pour produire diérentes séquences de test. Enn, la valeur par défaut autoseed assure de nombreuses
racines diérentes aux diérentes exécution du noyau. Les TP que vous rendrez seront bien évidemment
testés avec autoseed.

3     Questions préliminaires
Notez les réponses aux questions suivantes dans un chier texte questions.txt en identiant bien les
diérentes questions auxquelles vous répondez. Ce chier est à rendre avec le devoir.




                                                       2
3.1 Lecture de code
3.1.1   Threads

Question 1.       Qu'arrive-t-il à un thread lorsqu'il quitte (i.e. appelle thread_exit()) ? Et lorsqu'il
s'endort ?
Question 2.      Quelle(s) fonction(s) traite(nt) les changements de contexte ?
Question 3.      Quel est le rôle de chacun des états dans lequel un thread peut se trouver ?
Question 4. Que signie désactiver les interruptions ? Comment fait-on cela dans le code ? Pourquoi
et où est-ce utile dans le système de gestion de threads ?
Question 5.      Que se passe-t-il lorsqu'un thread veut réveiller un autre thread ?

3.1.2   Ordonnancement

Question 6.      Quelle est la fonction chargée de choisir le prochain thread à exécuter ?
Question 7.      Comment cette fonction choisit-elle ce prochain thread ?
Question 8. Quel rôle l'horloge matérielle joue-t-elle dans l'ordonnancement ? Quelle est la fonction
indépendante de l'architecture appelée sur une interruption d'horloge ?

3.1.3   Synchronisation

Question 9. Décrivez comment thread_sleep() et thread_wakeup() sont utilisés pour mettre en
oeuvre les sémaphores. Quel est le rôle de l'argument passé à la fonction thread_sleep() ?
Question 10.     Pourquoi l'API de verrous (mutex ) d'OS/161 propose-t-elle lock_do_i_hold() mais pas
lock_get_holder() ?


3.2 Exercices de synchronisation
3.2.1   Deadlock

Question 11. Le code ci-dessous consiste en deux threads qui utilisent des sémaphores binaires. Iden-
tiez une séquence d'exécution et de changements de contexte qui amènent ces deux threads dans une
situation de deadlock.

             semaphore *mutex, *data;

             void moi() {                                       void toi() {
               P(mutex);                                          P(data);
               /* faire des choses */                             P(mutex);
               P(data);
               /* faire d'autres choses */                          /* faire quelque chose */
               V(mutex);
               /* clean up */                                       V(data);
               V(data);                                             V(mutex);
             }                                                  }

Question 12. Proposez des modications du code qui rend impossible tout deadlock. Expliquez ce
qui amène ces deux threads dans un deadlock. En tirer un principe général à respecter pour éviter les
deadlocks.


                                                      3
3.2.2       Encore des deadlocks

Question 13.       Le code ci-dessous présente deux threads. Une situation de deadlock est-elle possible ?
Si c'est le cas, identiez une séquence d'exécution qui produit un tel deadlock ainsi qu'une modication
du code qui empêche tout deadlock.

        lock *fichier1, *fichier2, *mutex;

        void laurel() {                                     void hardy() {
          lock_acquire(mutex);                                /* faire des choses */

            /* faire des choses */                              lock_acquire(fichier1);
                                                                /* lire dans le fichier 1 */
            lock_acquire(fichier1);                             lock_acquire(fichier2);
            /* écrire dans le fichier 1 */                      /* écrire dans le fichier 2 */
            lock_acquire(fichier2);
            /* écrire dans le fichier 2 */                      lock_release(fichier1);
            lock_release(fichier1);                             lock_release(fichier2);
            lock_release(mutex);                                lock_acquire(mutex);

            /* faire d'autres choses */                         /* faire encore des choses */

            lock_acquire(fichier1);                             lock_acquire(fichier1);
            /* lire dans le fichier 1 */
            /* écrire dans le fichier 2 */                      /* écrire dans le fichier 1 */

            lock_release(fichier2);                             lock_release(fichier1);
            lock_release(fichier1);                             lock_release(mutex);
        }                                                   }


4       Implémentation
4.1 Primitives de synchronisation
4.1.1       Verrous

Après avoir lu le code mettant en oeuvre les sémaphores dans OS/161, écrivez le code correspondant à
la mise en oeuvre des verrous (mutex ). La documentation et l'API pour ces fonctions sont décrites dans
le chier kern/include/synch.h . Le code est à modier dans kern/threads/synch.c . Attention : il
est interdit d'utiliser les primitives de sémaphores pour mettre en oeuvre les verrous. Par contre, il est
tout à fait autorisé de s'inspirer du code de ces primitives, d'en recopier et d'en adapter des morceaux.
Pensez à utiliser le code de test fourni avec le noyau d'OS/161 (commande sy2).

4.1.2       Variables conditionnelles

Procédez de même pour mettre en oeuvre les variables conditionnelles dans OS/161. La documentation
et l'API sont elles aussi dans kern/include/synch.h et le code à modier dans kern/thread/synch.c .
Pensez à utiliser le code de test fourni avec le noyau d'OS/161 (commande sy3).

4.2 Problèmes : des chats et des souris
Une fois que vos implémentations des verrous et des variables conditionnelles fonctionnent, vous allez
pouvoir les utiliser pour résoudre (dans l'espace noyau toujours) le problème de synchronisation suivant.
Vous devez résoudre le problème 2 fois : une fois avec des sémaphores, et une autre fois avec des verrous
et des variables conditionnelles. Pensez à xer la racine du générateur de nombres pseudo-aléatoires pour
faciliter le débuggage de votre code.

                                                    4
Énoncé.     Dans une maison se trouvent réunis 6 chats et 2 souris. Les chats disposent pour se nourrir
de 2 gamelles. Les souris volent la nourriture des chats pour se nourrir, mais si un chat voit une souris
il la mange.
Votre but est de synchroniser chats et souris de sorte qu'aucune souris ne soit dévorée par un chat. Ainsi,
lorsqu'un chat est en train de manger à une gamelle, aucune souris ne doit s'approcher d'une gamelle,
sous peine d'être vue et mangée. D'autre part, aucun chat et aucune souris ne doivent mourir de faim :
ils doivent tous (alternativement) pouvoir accéder à une gamelle de temps en temps. À chaque instant,
une gamelle ne peut être occupée que par un seul animal (chat ou souris).

Résolution     Le code source à modier est situé dans kern/asst1/catsem.c et kern/asst1/catlock.c
selon que vous utilisez des sémaphores ou des variables conditionnelles. Les entrées de menu correspon-
dantes sont 1a et 1b. Pour l'instant le code ne fait que créer un thread par animal. Votre travail consiste à
modier ce code pour qu'il résolve le problème. Chaque animal doit s'identier lorsqu'il est lancé, annon-
cer quand il commence à manger à une gamelle et lorsqu'il termine de manger. Les chats doivent tester
s'ils peuvent manger une souris et si c'est le cas l'acher (normalement avec un code bien synchronisé
ça n'arrive jamais). Pour simuler le temps passé à manger, utilisez clocksleep().
Créez un chier texte probleme.txt, à rendre avec le devoir, qui explique le fonctionnement de vos
solution dans chacun des deux cas et comment elles évitent les cas de famine.

5    Fin du TP
Une fois le TP terminé et vos solutions vériées avec les tests fournis, préparez une archive contenant
vos noms et les chiers suivants :
- asst1.diff        un di des sources contenant vos modications (voir ci-dessous) ;
- asst1.tar.gz une archive complète du code source d'OS/161 exporté (voir ci-dessous) ;
- questions.txt vos réponses aux questions ;
- probleme.txt votre explication de vos solutions au problème.
Puis envoyez-la par mail à votre enseignant de TP avant la date limite. Tout retard sera sanctionné par
un retrait de points.
Pour créer le di et exporter le code source, commencez par installer votre version modiée du source
sur le serveur SVN :
~$ cd $CS161/os161
~/os161/os161$ svn commit -m 'fin TP 4'
Pour obtenir les sources modiés : identiez avec la commande svn log les numéros de version du début
et de la n de TP (en théorie 2 et 3) et utilisez la commande suivante avec les valeurs qui conviennent :
~/os161/os161$ svn diff -r2:3 > asst1.diff
Enn, pour exporter vos sources modiées :
~/os161/os161$ svn export -r3 $CS161/os161 /tmp/asst1
Vériez que le code source contient bien tout ce que vous avez modié. Une fois convaincu, il vous reste
à archiver le répertoire /tmp/asst1 contenant vos sources.




                                                     5

								
To top