Operating systems

Portail informatique

TP noté 2019-2020

Modalités

Durée : 3 heures

Tous documents autorisés.

Les exercices 2, 3 et 4 sont indépendants les uns des autres. Aussi, n'hésitez pas à lire tout le sujet avant de commencer pour déterminer l'ordre dans lequel vous souhaitez traiter les questions.

Le barème est donné à titre indicatif des poids entre les différents exercices.

Préparation

cd votreRepertoireDeTravailPourCSC4508 wget http://www-inf.telecom-sudparis.eu/COURS/CSC4508/Supports/CF/2019-2020/cf1/bKnJUPNnKG/TPNote2020.tgz tar xvfz TPNote2020.tgz mv TPNote2020 ${USER}_TPNote2020 cd ${USER}_TPNote2020

Livraison

La « livraison » de votre travail en fin de TP noté se fera par remontée sous Moodle (rubrique « TP noté de 3 heures ») du fichier d'extension tgz constitué de la manière suivante :

cd votreRepertoireDeTravailPourCSC4508 tar cvfz ${USER}.tgz ${USER}_TPNote2020

Outil d'analyse de performance (10 points)

Dans le répertoire exercice2/, on vous fourni le code de libmutex, qui compte le nombre d'appels aux fonctions pthread_mutex_* que fait une application.

Cet outil fonctionne sur le même principe que l'outil développé lors du TP 11: il intercepte les appels aux fonctions voulues, et met à jour des statistiques. Lorsque l'application se termine, les statistiques collectées sont affichées.

Le programme exercice2/test/test_program.c permet de tester l'outil. Après compilation, utilisez LD_PRELOAD pour intercepter les appels de test_program aux fonctions pthread_mutex_*:

$ LD_PRELOAD=../libmutex/libmutex.so ./test_program nb_threads=1, cpt = 10000 Nb mutex lock: 10000 Nb mutex trylock: 0 Nb mutex unlock: 10000 Nb mutex init: 1 Nb mutex destroy: 0

Programme de test (3 points)

Le programme de test ne fonctionne actuellement qu'avec un thread.

Dans le répertoire exercice2/q1/, modifiez le programme pour qu'il crée plusieurs threads qui exécutent chacun la fonction test_function lorsque l'utilisateur donne un nombre de threads en argument. Une fois les threads créés, le thread principal attend que tous les threads soient terminés, puis il affiche la valeur du compteur cpt.

Thread-safety (2 points)

L'implémentation actuelle de libmutex n'est pas thread-safe: lorsqu'on collecte des données avec le programme de test multi-threadé, le nombre d'appels à pthread_mutex_lock que l'outil recence est incorrect:

$ LD_PRELOAD=libmutex.so ../q1/test_program 4 nb_threads=4, cpt = 40000 Nb mutex lock: 39443 Nb mutex trylock: 0 Nb mutex unlock: 40000 Nb mutex init: 1 Nb mutex destroy: 0

Dans le répertoire exercice2/q2/, modifiez mutex.c afin de corriger le problème de thread-safety et vérifiez que les données collectées par l'outil sont correctes.

statistiques par thread (5 points)

On souhaite maintenant que libmutex collecte des statistiques pour chaque thread de l'application.

Recopiez le fichier exercice2/q2/mutex.c dans exercice2/q3/mutex.c. Modifiez ensuite le fichier pour que chaque thread ait une instance de la structure struct mutex_stats. Dans la fonction initialize_thread (qui est appelée la première fois qu'un thread collecte une statistique), chaque thread doit "s'enregistrer" dans une liste chaînée. Pour cela, créez une structure contenant les informations du thread: identifiant du thread (obtenu avec pthread_self()), et adresse du struct mutex_stats dans lequel le thread va collecter ses données. Insérez ensuite cette structure dans une liste chaînée partagée entre tous les threads.

Lorsque l'application se termine, parcourez la liste chaînée et appelez la fonction print_mutex_stats pour chaque thread enregistré.

Bank (7 points)

Le répertoire exercice3/bank contient un ensemble de programmes permettant de simuler la gestion de comptes en banque:

  • accounts.dat: fichier contenant les soldes d'un ensemble de comptes en banque. Chaque solde est stocké sous la forme d'un struct account (défini dans account.h)
  • balance.c: programme qui calculera la balance de la banque, c'est à dire la somme de tous les soldes. Ce programme est à compléter dans la question q1
  • transfer.c: programme qui effectue un transfert d'argent entre deux comptes
  • init_accounts.sh: script qui initialise un ensemble de compte
  • stress_accounts.sh: script qui effectue un grand nombre de transferts entre comptes en parallèle

2 points

Complétez le code du programme q1/balance.c. Ce programme ouvre le fichier dont le nom est passé et paramètre, lit les struct account qui y sont stockés, affiche les différents comptes et calcule la somme de tous les champs account_balance avant d'afficher la somme.

Voici un exemple d'affichage produit par le programme:

$ ./balance accounts.dat [0] Hector le castor -> 241899 [1] Edouard le canard -> 160065 [2] José le sanglier -> 134121 [3] Charlotte la marmotte -> -363567 [4] Mireille l'abeille -> 113807 [5] Léon le frelon -> -81699 [6] Fédor le porc -> -335466 [7] Tonio le blaireau -> -98359 [8] Yvan le hareng -> 399507 [9] Edgar le cougar -> 12386 Balance: 182694

3 points

Complétez le code du programme q2/transfer.c. Ce programme ouvre le fichier dont le nom est stocké dans la variable account_file, puis lit le contenu des comptes situés aux indices src et dest. Le programme appelle ensuite la fonction transfer (qui transfère une somme d'argent d'un compte à l'autre), puis écrit les nouveaux contenus des comptes dans le fichier.

Vérifiez que la balance de la banque est inchangée après un transfert. Par exemple:

$ ./balance accounts.dat [0] Hector le castor -> 241899 [1] Edouard le canard -> 160065 [2] José le sanglier -> 134121 [3] Charlotte la marmotte -> -363567 [4] Mireille l'abeille -> 113807 [5] Léon le frelon -> -81699 [6] Fédor le porc -> -335466 [7] Tonio le blaireau -> -98359 [8] Yvan le hareng -> 399507 [9] Edgar le cougar -> 12386 Balance: 182694 $ ./transfer accounts.dat 8 9 10000 New balance for Yvan le hareng: 389507 New balance for Edgar le cougar: 22386 $ ./balance accounts.dat [0] Hector le castor -> 241899 [1] Edouard le canard -> 160065 [2] José le sanglier -> 134121 [3] Charlotte la marmotte -> -363567 [4] Mireille l'abeille -> 113807 [5] Léon le frelon -> -81699 [6] Fédor le porc -> -335466 [7] Tonio le blaireau -> -98359 [8] Yvan le hareng -> 389507 [9] Edgar le cougar -> 22386 Balance: 182694

2 points

Lorsqu'on transfère de l'argent d'un compte à l'autre, la balance totale de la banque est inchangée. Pourtant le script stress_accounts.sh qui effectue un grand nombre de transactions en parallèle détecte un problème: la balance après toutes les transactions est différente de la balance avant les transactions.

Recopiez le programme q2/transfer.c dans le répertoire q3, puis modifiez le pour corriger les problèmes de concurrence. Pour cela, il est nécessaire de verrouiller le fichier afin d'empêcher les écritures concurrentes sur les parties du fichier modifiées lors du transfert. Assurez vous que le scriptstress_accounts.sh ne détecte plus de problème.

Si vous verrouillez plusieurs parties du fichier (par exemple, celles correspondant aux comptes A et B), vous risquez de générer un deadlock. Pour éviter ce deadlock, une solution consiste à prendre les verrous en commençant par le numéro de compte le plus petit. Ainsi, les verrous sont toujours pris dans le même ordre et on évite les cycles de dépendances qui entrainent le deadlock.

XV6 - Ordonnancement de processus (5 points)

Commencez par donner le code de xv6 que vous avez réalisé lors du TP5 (implémentation d'un ordonnanceur). Pour cela, exécutez les commandes suivantes en les adaptant :

cd repertoire_xv6_TP5 make clean cd votreRepertoireDeTravailPourCSC4508 cp -r repertoire_xv6_TP5 exercice4/xv6_code_etudiant

Si vous n'avez pas fait le TP5 ou que le code produit n'est pas utilisable, vous pouvez vous baser sur le corrigé en exécutant les commandes suivantes à la place:

git clone https://gitlab.inf.telecom-sudparis.eu/csc4508/xv6-tsp.git git remote add scheduler-soluce https://gitlab.inf.telecom-sudparis.eu/csc4508/xv6-soluces/xv6-soluce-scheduler.git git fetch --all git checkout -b scheduler-soluce -t scheduler-soluce/scheduler-lab

2 points

Dans xv6, ajoutez le champ int prio à la structure struct proc, et ajoutez l'appel système int set_priority(int pid, int priority). Cet appel système vérifie que le processus PID existe (c'est à dire que son état n'est pas UNUSED), et change la priorité du processus dont le PID est pid et affiche le message "Process <pid> now runs with priority <priority>".

1 point

Dans xv6, ajoutez le programme utilisateur renice.c qui prend deux argument pid et priority, et qui utilise l'appel système set_priority pour changer la priorité du processus pid.

2 points

Dans le fichier exercice4/reponses.txt, expliquez ce qu'il se passe lorsque le système reçoit un tick (c'est à dire le signal IRQ_TIMER), et comment un nouveau processus est ordonnancé. Cette présentation doit expliquer le rôle des différentes fonctions parcourues et les mécanismes évitant les problèmes (détection d'erreur, verrouillage, etc.)