Département INFormatique 
  CSC4508/M2 : Concepts des systèmes d'exploitation et mise en œuvre sous Unix


    Contenu



Entrées-sorties

Corrigés

Exercice 1 : Nombre d'octets à écrire et performances

Préparation

Se placer dans un répertoire de travail
tar xvfz perfW.tgz
cd PerfW

Question 1

Compléter toutes les sections "/* A vous de compléter */" du fichier pw.c
make

Question 2

Ca y est : "pw.c compile" !
/usr/bin/time pw 16
Puis 32, 64 et toutes les puissances de 2 jusqu'à 8192. Pour automatiser ce traitement, on peut s'appuyer sur les capacités de bash:
  • for i in 16 32 64 128 256 512 1024 2048 4096 8192; do echo $i; /usr/bin/time pw $i; done
  • ou bien
    for ((i=16; i<= 8192; i*=2)); do echo $i; /usr/bin/time pw $i; done
Qu'en concluez-vous ?

Question 3

Rajouter l'option O_SYNC à votre fonction open()
/usr/bin/time pw 512
/usr/bin/time pw 8192
Qu'en pensez-vous ?

Question 4

Recopier pw.c en pwWritev.c
Modifier pwWritev.c en :
  • Retirant l'option O_SYNC de la fonction open(),
  • Ecrivant le fichier à l'aide de la fonction writev (au lieu de write). writev devra être appelé avec, en paramètre, 8192/param_appel_a_pwWritev structures iovec pointant sur des zones de param_appel_a_pwWritev octets.
Compiler pwWritev
Comparer les performances de pw 16, pw 8192, pwWritev 16, et pwWritev 8192

Exercice 2 : Comment écrire par blocs de 8192 octets alors que l'application écrit par blocs de 16 octets et ce qu'on y gagne

Préparation

Se placer dans un répertoire de travail
tar xvfz cmpW.tgz
cd CmpW

Question 1

Dans cw.c, comparer ecritMethode1() et ecritMethode2().
Pourquoi à la fin de ecritMethode2() doit-on appeler flushBuf() ?

Question 2

On souhaite comparer ces 2 méthodes d'écriture. Pour ce faire, on va "instrumenter" (faire un "profiling", si on utilise la terminologie anglaise) le programme cw.
Taper
    make cw
Que fait cette instruction ?
Noter que :
  • L'option -pg qui demande l'instrumentation
Taper
    make run
Que fait cette instruction ?
Noter que:
  • "/usr/bin/time cw" exécute le programme et génère un fichier contenant les résultats du "profiling" : gmon.out
  • "gprof cw > resultat.txt" génère une version compréhensible par un humain du fichier gmon.out, génération qu'on redirige vers le fichier resultat.txt
Voici un exemple commenté du fichier resultat.txt (les commentaires sont signalés par des "//"). Consulter le pour voir le type d'informations qu'on peut exploiter avec ce profiling.
Analyser (rapidement) votre propre fichier resultat.txt.

Question 3

Mêmes manipulations qu'à la question 2 en ajoutant O_SYNC au moment du open des fichiers *et* en diminuant d'au moins un facteur 50 la taille des fichiers tests générés.

Exercice 3 : Calcul du nombre de lignes

  • Écrire un programme qui lit un fichier et affiche le nombre de lignes de ce fichier.
  • Compléter ce programme pour que le nom du fichier soit passé en argument.
  • Ajouter un test sur la nature de cet argument : s'il s'agit d'un fichier, compter et afficher son nombre de lignes, sinon, afficher seulement son type : répertoire ou fichier spécial.
  • Compléter ce programme de la façon suivante : quand l'argument est un répertoire, il faut alors se déplacer sous ce répertoire, et afficher le nombre de lignes de tous les fichiers ordinaires qu'il contient et seulement le nom pour les autres entrées.
Remarque : il faut penser à tester l'exécution des fonctions (par exemple si le répertoire est inaccessible, la fonction chdir échoue).

Exercice 4 : Informations SGF

  • Écrire un programme qui sera exécuté avec en argument un nom de fichier ou de répertoire. Ce programme affichera alors le nombre de blocs total, le nombre de blocs libres, le nombre d'inodes total et le nombre d'inodes libres du système de fichiers auquel appartient cet argument.
Les résultats de ce programme pourront être vérifiés avec les résultats de la commande df. (attention : selon les versions, df utilise des blocs de 512 ou de 1K octets).

Exercice 5 : Méthode pour trouver "instantanément" la i-è ligne d'un fichier texte

On se propose de développer un programme gl.c (pour GotoLigne) permettant de trouver "instantanément" la ième ligne d'un fichier texte dont les lignes ont des longueurs différentes (inférieures à 1024 caractères, caractère de nouvelle ligne inclus).
Le principe de la solution retenue est le suivant pour trouver la iè ligne du fichier mEtMme.txt :
  • Avant l'utilisation de gl.c, on construit (à l'aide du programme d'initialisation gli) un fichier mEtMme.txt.idx tel que :
    • Les 4 premiers octets du fichier mEtMme.txt.idx contiennent le décalage écrit en binaire de la 1ère ligne de mEtMme.txt (par rapport au début de mEtMme.txt), i.e. 0x00000000 (zéro).
    • Les 4 suivants contiennent le décalage de la 2ème ligne de mEtMme.txt, i.e. longueur de la 1ère ligne. Par exemple, 0x07000000 (et non 0x00000007, car le processeur Intel est Little Endian ; NB: vous n'aurez rien de spécial à faire dans votre programme pour prendre en compte le Little Endian) si la 1ère ligne de mEtMme.txt contient "Coucou\n".
    • Les 4 suivants contiennent le décalage de la 3ème ligne de mEtMme.txt, i.e. longueur de la 1ère ligne + longueur de la 2ème. Par exemple, 0x0d000000, si la 2ème ligne contient "Hello\n".
  • Au moment de l'exécution de gl.c :
    • On ouvre le fichier mEtMme.txt.idx
    • Grâce à lseek on se décale pour se placer au niveau des octets correspondant au décalage du numéro de ligne cherchée
    • On lit, dans un entier, les 4 octets correspondant au numéro de ligne cherchée
    • On lit également les 4 octets correspondant au numéro de ligne recherché + 1 (on connaît ainsi la longueur de la ligne cherchée)
    • On ouvre le fichier mEtMme.txt
    • On se décale de la valeur des 4 octets correspondant à la ligne cherchée
    • On lit le fichier pour récupérer toute la ligne

Question 1

Écrire le programme d'initialisation "gli" (à l'aide des fonctions de la bibliothèque C d'entrées-sorties). Ce programme lit sur la ligne de commande le nom du fichier pour lequel il faut générer le fichier d'index (le nom du fichier d'index est le nom du fichier original concaténé avec ".idx").

Question 2

Écrire le programme "gl" (à l'aide des fonctions de la bibliothèque C d'entrées-sorties). Ce programme lit sur la ligne de commande le nom du fichier et le numéro de ligne. Il affiche la ligne.

Question 3

Écrire une nouvelle version de "gl" (glMmap) qui utilise mmap pour permettre la lecture des octets du fichier d'index sans utiliser fread ou read.

Exercice 6 : Accès concurrents et NFS

Question 1

Écrire un programme "nfs" qui :
  • Lit sur la ligne de commande :
    • le nom du fichier à ouvrir
    • une chaîne de caractère(s).
  • Ouvre le fichier en mode écriture seulement, création  si n'existe pas et mode "ajout" (droits lecture/écriture pour l'utilisateur seulement).
  • Écrit 10.000.000 de fois la chaîne de caractères dans le fichier.

Question 2

Lancer la commande "nfs /var/tmp/ficTest a" sur 2 terminaux différents en parallèle. Faire "ls -l /var/tmp/ficTest". Que pensez-vous de la taille du fichier obtenu ? Effacer le fichier /var/tmp/ficTest

Question 3

Même opération avec le fichier ~/ficTest avec:
  1. les 2 terminaux étant sur la même machine (ne pas oublier d'effacer le fichier obtenu) : pourquoi a-t-on le même comportement qu'à la question 1 ?
  2. les 2 terminaux sur une machien différente : pourquoi perd-on des écritures ?

Exercice 7 : Gestion d'une banque

Préparation

Se placer dans un répertoire de travail
tar xvfz banque.tgz
cd Banque

Recommandation

Dans cette exercice, vous allez écrire du code. Et ce code, vous allez le tester pour le vérifier. Il est recommandé d'effecturer ces tests avec un numéro de compte autre que le compte numéro 0 (zéro) pour éviter de passer à côté de certaines erreurs (impossibles à voir avec le compte numéro 0).

Question 0

On souhaite écrire un programme pour la gestion d'une banque. Ce programme manipule un fichier de données ("banque.dat") contenant NBCOMPTES comptes en banque. Chaque compte est identifié par un numéro correspondant au rang du compte dans le fichier (par exemple, le compte numéro 0 apparaît en premier dans le fichier) et est représenté dans le fichier par un nombre décimal (éventuellement négatif) correspondant au solde du compte.

Dans cette question, on suppose que NBCOMPTES vaut 4 et que les 4 comptes ont respectivement pour solde : -10,02 euros, 1235,43 euros, 150000,43 euros et 9999999,99 euros.

On considère les trois implémentations suivantes du fichier :
  • Implémentation 1 :
    -10.02[caractère(s) de renvoi à la ligne]
    1235.43[caractère(s) de renvoi à la ligne]
    150000.43[caractère(s) de renvoi à la ligne]
    9999999.99[caractère(s) de renvoi à la ligne]
     
  • Implémentation 2 :
        -10.02[caractère(s) de renvoi à la ligne]
       1235.43[caractère(s) de renvoi à la ligne]
     150000.43[caractère(s) de renvoi à la ligne]
    9999999.99[caractère(s) de renvoi à la ligne]
     
  • Implémentation 3 :
    XXXXXXXXYYYYYYYYZZZZZZZZTTTTTTTT
    où X (respectivement Y, Z et T) représente 1 octet, XXXXXXXX (respectivement YYYYYYYY, ZZZZZZZZ et TTTTTTTT) correspondant à la valeur binaire du double contenant le solde du compte numéro 0 (respectivement 1, 2 et 3). 
Dans le fichier reponses.txt, calculez (en justifiant brièvement) pour chacune de ces implémentations :
  • Le nombre d'octets requis pour stocker les 4 comptes (pour les implémentations 1 et 2, on distinguera le cas Unix et le cas Windows)
  • Le nombre d'octets qui doivent être lus sur disque pour disposer en mémoire du solde du compte numéro 3 (traiter uniquement le cas Unix).
Dans les questions suivantes, on décide de privilégier l'implémentation 3 qui minimise le nombre d'octets et le nombre de lectures de fichier requises. Vous pouvez réaliser cette implémentation soit avec les fonctions système d'accès aux fichiers (read, write...), soit avec les fonctions de la librairie C (fread, fwrite..). Mais dans le cas des fonctions de la librairie C, veillez à inhiber le tampon d'écriture des fichiers (avec setvbuf) sous peine d'avoir des anomalies de fonctionnement.

Question 1

Compléter le fichier banque.c de manière à ce qu'il implémente l'algorithme suivant :

Lecture des paramètres d'appel du programme
Ouverture fichier "banque.dat"
Lecture du solde stocké dans le fichier
Affichage de ce solde
sleep(5)
si (typeOp == 'L') alors
   Lecture du solde stocké dans le fichier
   Affichage du solde
sinon
   Calcul du nouveau solde
   Ecriture de ce nouveau solde dans le fichier
finsi
Fermeture du fichier


NB : si le fichier "banque.dat" n'existe pas, il faut le créer en l'initialisant avec NBCOMPTES valeurs de type double, chacune étant initialisées à 0 et correspondant au solde de chacun des comptes gérés par la banque.

Vérifier le fonctionnement correct de l'application.

Question 2

Ouvrir une deuxième fenêtre et placez-vous également dans le répertoire Banque de votre répertoire de travail
Taper "banque 0 L" ("L" comme "Lecture") dans la première fenêtre (sans taper return)
Taper "banque 0 M -5" ("M" comme "Modification" ou "Mise-à-jour" dans la deuxième fenêtre et appuyer sur return
Revenir dans la première fenêtre et appuyer sur return
Pourquoi "banque 0 L" affiche 2 soldes différents au cours de son exécution ? Ce comportement est-il acceptable par un client ? Pour ces 2 questions, vous veillerez à répondre dans le fichier reponses.txt.

Question 3

Taper "banque 0 M -10" dans la première fenêtre (sans taper return)
Taper "banque 0 M -5" dans la deuxième fenêtre et appuyer sur return
Revenir dans la première fenêtre et appuyer sur return
Attendre la fin de l'exécution de ces 2 programmes, puis exécuter "banque 0 L".
Commenter le résultat. Ce comportement est-il acceptable par un client ? Vous veillerez à répondre dans le fichier reponses.txt.

Question 4

On souhaite corriger les dysfonctionnements observés aux questions 2 et 3. Pour ce faire, on souhaite tester une solution consistant en la pose du verrou le plus adapté aux circonstances. L'algorithme envisagé est le suivant :

Lecture des paramètres d'appel du programme
Ouverture fichier "banque.dat"
Pose verrou en lecture // Nouveau par rapport a la question 1
Lecture du solde stocké dans le fichier
Affichage de ce solde
sleep(5)
si (typeOp == 'L') alors
   Lecture du solde stocké dans le fichier
   Affichage du solde
sinon
   Pose verrou en écriture
// Nouveau par rapport a la question 1
   Calcul du nouveau solde
   Ecriture de ce nouveau solde dans le fichier
finsi
Relâchement verrou
// Nouveau par rapport a la question 1
Fermeture du fichier

Faire la commande cp banque.c banqueQ4.c
Implémenter l'algorithme ci-dessus dans la banqueQ4.c
Vérifier que les manipulations de la question 2 n'entraînent plus de dysfonctionnement.
Constater qu'en revanche, les manipulations de la question 3 donnent lieu à une erreur. Expliquer, dans le fichier reponses.txt, le message d'erreur.

Question 5

On souhaite corriger le dysfonctionnement observé à la question 4. Une personne propose l'algorithme suivant :

Lecture des paramètres d'appel du programme
Ouverture fichier "banque.dat"
Pose verrou en lecture
Lecture du solde stocké dans le fichier
Affichage de ce solde
sleep(5)
si (typeOp == 'L') alors
   Lecture du solde stocké dans le fichier
   Affichage du solde
sinon
  
Relâchement verrou // Nouveau par rapport a la question 4
   Pose verrou en écriture
   Calcul du nouveau solde
   Ecriture de ce nouveau solde dans le fichier
finsi
Relâchement verrou

Fermeture du fichier

Indiquez dans le fichier reponses.txt ce que vous pensez de cette proposition. NB : il n'est pas demandé d'écrire un programme banqueQ5.c, même si ça peut aider.

Question 6

Pour corriger tous les dysfonctionnements, une personne propose l'algorithme suivant (consistant à poser dès le début le verrou le plus fort dont on aura besoin) :

Lecture des paramètres d'appel du programme
Ouverture fichier "banque.dat"
si (typeOp == 'L') alors // Nouveau par rapport a la question 4
   Pose verrou en lecture // Nouveau par rapport a la question 4
sinon                     // Nouveau par rapport a la question 4
   Pose verrou en écriture// Nouveau par rapport a la question 4
finsi                     // Nouveau par rapport a la question 4
Lecture du solde stocké dans le fichier
Affichage de ce solde
sleep(5)
si (typeOp == 'L') alors
   Lecture du solde stocké dans le fichier
   Affichage du solde
sinon
   // Nouveau par rapport a la question 4 (plus de "Pose verrou en écriture")
   Calcul du nouveau solde
   Ecriture de ce nouveau solde dans le fichier
finsi
Relâchement verrou

Fermeture du fichier

Faire la commande cp banque.c banqueQ6.c
Implémenter l'algorithme ci-dessus dans la banqueQ6.c
Vérifier qu'il n'y a plus de dysfonctionnement.

Question 7

banqueQ7 a en fait encore un dysfonctionnement !
Ouvrir une troisième fenêtre :
  • Taper "banque 0 M -10" dans la première fenêtre (sans taper return)
  • Taper "banque 0 L" dans la deuxième fenêtre (sans taper return)
  • Taper "banque 0 L" dans la troisième fenêtre et appuyez sur return
  • Aller dans la première fenêtre et appuyer sur return
  • Aller dans la deuxième fenêtre et appuyer sur return
  • Dès que "banque 0 L" a fini dans la troisième fenêtre, relancer le.
  • Dès que "banque 0 L" a fini dans la deuxième fenêtre, relancer le.
  • On observe que "banque 0 M -10" n'arrive jamais à s'exécuter !
Ce dysfonctionnement ne pourra être corrigé que lors de l'exercice CommInterProcessus/7



Page mise à jour le 28 mars 2012