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
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 :
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_*:
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:
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:
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:
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.
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 :
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:
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.)