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


    Contenu



Gestion de la mémoire

Corrigés

Exercice 1 : L'écroulement (thrashing)

Préparation

Se placer dans un répertoire de travail
tar xvfz ecroul_4Go.tgz
cd Ecroul_4Go

Question 1

cache_line.c et alea.c initialisent un tableau de N méga octets. Comparer leur manière de travailler.

Question 2

Lancer les programmes cache_line et alea et comparer leurs performances. Par défaut ces programmes utilisent des tableaux de 3 Mo.

Question 3

Déterminer à l'aide de la commande 'free -m', la quantité de mémoire physique disponible et la taille de l'espace disque disponible pour le swap (variable selon le type de PC utilisé).

Question 4

La commande vmstat permet de voir l'évolution des processus et du système dans le temps. Elle affiche à l'intervalle de temps choisi divers paramètres utiles pour cet exercice :
- r : nombre de processus "runnable" (vmstat lui-même n'est pas compté)
- b: nombre de processus bloqués en attente disque
- fre: mémoire libre disponible (en Ko)
- si: "swap in" lecture de pages sur le disque (en Ko/s)
- so: "swap out" écriture de pages sur le disque (en Ko/s)
- id: "idle" pourcentage de temps pendant lequel la CPU est inutilisée

Dans une fenêtre 1, exécuter la commande vmstat 1 (le 1 signifie 1 affichage par seconde)
Dans une fenêtre 2:
./alea n

n correspond à partieEntiere(0.25 x quantité de mémoire physique libre)
Commenter l'évolution des champs r, b, free, si, so, id (dans la fenêtre 1) et les différences de temps d'exécution entre les différents processus.
Au vu de la valeur de n, à combien de défauts de pages mineurs pouvait-on s'attendre ?

Question 4

Dans la fenêtre 1, exécuter la commande ./use_memory n (avec n=partieEntiere(0.5 x quantité de mémoire physique libre)
Dans la fenêtre 2, lancer la commande ./alea m (avec m=partieEntiere(0.25 x quantité de mémoire physique libre)
Même manip avec m=partieEntiere(0,5 x quantité de mémoire physique totale).

Exercice 2 : Remplacement de pages

Préparation

Se placer dans un répertoire de travail
tar xvfz remplacementDePages.tgz
cd RemplacementDePages

Question 1

Un processus référence successivement les pages 0, 1, 2, 0, 1, 0, 2, 0. Il dispose d'une mémoire avec 2 cadres de page initialement vides.

Indiquer, dans le fichier anomalieDeBelady.txt, les séquences de défauts de page générés par ce processus, pour chacun des trois algorithmes de remplacement de pages : FIFO, LRU, et Optimal.

Exercice 3 : De l'alignement des structures

Préparation

Se placer dans un répertoire de travail
tar xvfz align.tgz
cd Align
make

Le module useGetrusage.c facilite l'utilisation de la fonction getrusage. Etudier le "man getrusage" pour bien comprendre le rôle de cette fonction. Puis analyser useGetrusage.c pour comprendre son fonctionnement.

Question 1

Comparer la structure tableElement des différents fichiers align.c, nonAlign.c, nonAlignPacked.c
Avec diff, comparer nonAlign.c et nonAlignPacked.c

Question 2

Exécuter align, nonAlign et nonAlignPacked
Interpréter les résultats

Question 3

Dans cette question, au vu du comportement de nonAlign, on cherche à comprendre pourquoi, par défaut, le compilateur cherche à avoir les feuilles des structures alignées sur des frontières de 4 octets.
Pour ce faire, il faut étudier la traduction assembleur de l'instruction :
    t[i].unInt = UNEVALEURQUELCONQUE;
Rechercher dans align.s, nonAlign.s et nonAlignPacked.s l'instruction assembleur correspondant à l'instruction C ci-dessus (au fait, quelle instruction du Makefile a généré l'assembleur ?).
Au vu de la différence de performances entre align et nonAlignPacked, qu'en pensez-vous ?

Question 4

Dans la question 3, on a observé que align et nonAlignPacked avaient des performances similaires. Dans cette question, on va montrer que si on est sur une frontière de 16 octets (liée aux instructions MMX, cf. exercice 8), nonAlignPacked est légèrement moins performant que align.
Analyser le fichier testAlign.c. Que fait-il ?
Exécuter plusieurs fois testAlign.
Dans le Makefile, supprimer -DALIGN, sauvegarder et taper la commande : make
Exécuter plusieurs fois testAlign.
Commenter les différences de performance observées

Question 5

Quelle règle de programmation pouvez-vous vous fixer par rapport aux structures ?

Exercice 4 : Algorithme des "frères siamois" (Buddy System)

Soit un système utilisant l'agorithme des frères siamois pour gérer la mémoire. Initialement, la mémoire consiste en un bloc de 256K.
  • Représenter l'état d'occupation de la mémoire après les placements successifs des processus A(5K), B(25K), C(35K), et D(20K) puis les terminaisons successives des processus dans l'ordre A, D, C, et B.

Exercice 5 : Allocation avec le Buddy System

Soit un système utilisant l'algorithme des frères siamois pour gérer la mémoire.
  • Ecrire de façon schématique la procédure allocation(T) qui correspond à une demande d'allocation d'une zone mémoire de taille T et dont la réponse est soit l'adresse du buddy alloué, soit -1 si l'allocation est impossible.

  • On suppose que le système gère une liste pour chaque puissance de i utilisée et on ne demande pas de détailler la gestion de cette liste (on se limitera à écrire par exemple "insérer l'élément dans la liste i").

Exercice 6 : Anomalie de Belady

On appelle "anomalie de Belady" le fait que le nombre de défauts de pages augmente quand on augmente le nombre de cadres de pages disponibles.
On appelle "algorithme à pile" un algorithme présentant la propriété d'inclusion : c'est-à-dire, S(N,C) inclus dans S(N+1,C) quelle que soit la chaîne de références de pages C. S(N,C) représente l'ensemble des pages présentes en mémoire après traitement de la chaîne C lorsque l'on dispose de N cadres de page.
Une condition suffisante pour qu'un algorithme ne présente pas l'anomalie de Belady est qu'il soit à pile.
  • Montrer que LRU est à pile.
  • Montrer l'anomalie de Belady avec l'algorithme FIFO avec N=3 puis N=4, sur l'exemple suivant : C = { 1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5 }.

  • Retrouver sur cet exemple des états pour lesquels la propriété d'inclusion n'est pas vérifiée.

Exercice 7 : Profiling d'un allocateur "maison"

Préparation

Placez-vous dans un répertoire de travail
tar xvfz cmpAlloc.tgz
cd CmpAlloc

Question 1

Complétez les parties "/* A completer */" dans le fichier monAllocateur.c
NB : Ne vous occupez pas, pour l'instant, des "* A completer au moment de la question 2 *". Vous vous en occuperez... au moment de la question 2.
Tapez make pour le compiler

Question 2

Exécutez le programme "bug". La troisième ligne qu'il affiche signale un bug :
s2 = "1234567890123456"
s1 = "1234567890123456"
s2 = "" ==> BUG !

Exécutez : valgrind ./bug
Valgrind ne détecte aucun problème.

Dans monAllocateur.c, complétez les "* A completer au moment de la question 2 *" pour expliquer : a. l'origine de ce bug, b. la raison pour laquelle valgrind utilisé sur le programme bug ne détecte rien, et enfin c. quelle fonction utilisée en lieu et place de strcpy aurait évité ce bug.

Question 3

Exécutez ./cmpAlloc 100000000 1
Tapez gprof cmpAlloc
Les premières lignes vous indiquent le temps cumulé passé dans chaque fonction. Par exemple, nous avons obtenu l'affichage suivant sur une machine Dell Precision T3500 computers, équipée d'un processeur Intel Xeon W3530 (2.80 GHz) et de 4 GiB of RAM, avec un système d'exploitation Linux 3.14.9 SMP :
  %   cumulative   self              self     total          
 time   seconds   seconds    calls   s/call   s/call  name   
 50.21      2.06     2.06        1     2.06     2.06  travailAvecMalloc
 44.58      3.89     1.83        1     1.83     2.05  travailAvecMonMalloc
  2.82      4.00     0.12 50000001     0.00     0.00  monFree
  2.69      4.11     0.11 50000001     0.00     0.00  monMalloc
  0.12      4.12     0.01        1     0.01     0.01  monAllocateurInit

gprof
nous indique qu'avec un total de 2,06 secondes, travailAvecMalloc est aussi performant que travailAvecMonMalloc qui requiert un total de 2,05 secondes. Ce résultat contre-intuitif est dû au fait que les fonctions malloc et free n'ont pas été prises en compte ! Pour qu'elles soient prises en compte, il faut faire une édition de lien avec la version profilée de la bibliothèque standard C. Donc :
  1. Editez le Makefile
  2. Modifiez la ligne concernant LDFLAGS avec : LDFLAGS = -pg -static -lc_p
  3. make cleanall ; make
    2 cas peuvent se présenter :
    1. Vous n'avez pas d'erreur à l'édition de liens : passez au point 4.
    2. Vous avez une erreur "/usr/bin/ld: ne peut trouver -lc_p" : la version profilée de la bibliothèque C n'est pas installée sur votre système. Nous supposons que vous n'avez pas les droits root sur la machine que vous utilisez. Nous allons donc mettre en oeuvre un moyen d'obtenir cette version profilée de bibliothèque sans avoir besoin de l'installer :
      1. cd /tmp
      2. Récupérez une version récente de la bibliothèque C de GNU : wget http://ftp.gnu.org/gnu/glibc/glibc-2.21.tar.gz
      3. tar xvfz glibc-2.21.tar.gz
      4. mkdir glibc-build
      5. cd glibc-build
      6. ../glibc-2.21/configure --prefix=$HOME/Software/glibc --enable-profile
      7. Si cette commande vous affiche :
        configure: error:
        *** LD_LIBRARY_PATH shouldn't contain the current directory when
        *** building glibc. Please change the environment variable
        *** and run configure again.
        tapez : export LD_LIBRARY_PATH=
        puis : ../glibc-2.21/configure --prefix=$HOME/Software/glibc-2.21 --enable-profile
      8. make # Comptez environ 15 minutes de compilation
      9. make install
      10. Revenez dans le répertoire CmpAlloc
      11. Editez le makefile
      12. Modifiez la ligne concernant LDFLAGS avec : LDFLAGS = -pg -static -L${HOME}/Software/glibc-2.21/lib -lc_p
      13. make cleanall;make
  4. Exécutez ./cmpAlloc 100000000 1
  5. Tapez gprof cmpAlloc
Dans monAllocateur.c, sachant que le temps passé en malloc est le temps passé en malloc plus le temps passé en _int_malloc (regardez le call graph pour vous en convaincre !) et que le temps passé en free est le temps passé en _int_free + le temps passé en cfree, complétez les "* A completer au moment de la question *3* *"

Livrez votre fichier monAllocateur.c final selon les modalités qui vous ont été indiquées.

Exercice 8 : Contraintes et gains liés aux instructions MMX/SSE

Préparation

 Se placer dans un répertoire de travail
tar xvfz instructionsSSE.tgz
cd InstructionsSSE

Question 1

Le fonction computeResult du programme calculV0.c réalise des calculs sur les éléments de deux tableaux de flottants pArray1 et pArray2 de taille SIZE. Le résultat est stocké dans pResult.
On se propose d'accélérer l'exécution de ce programme en utilisant les instructions SSE du processeur Intel pour faire les calculs 4 flottants par 4 flottants.
SSE est :
  1. une collection de registres de 128 bits,
  2. un jeu d'instructions qui permet de voir ces registres comme 4 éléments (entier, flottant) de 32 bits et faire des opérations sur ces 4 entiers simultanément. On peut accéder aux instructions SSE soit en programmant directement en assembleur, soit en utilisant des fonctions intrinsèques (intrinsics), c'est-à-dire des fonctions que le compilateur sait directement traduire en une (ou plusieurs) instruction(s) assembleur (voir http://neilkemp.us/src/sse_tutorial/sse_tutorial.html pour plus d'informations).
calculV1.c (respectivement calculV2.c) est une version de calculV0.c exploitant des instructions SSE. Il travaille sur des tableaux alloués statiquement (respectivement dynamiquement). Vérifier que le Makefile prend en compte le commentaire en tête de calculV1.c et calculV2.c.
Taper make pour compiler tous ces programmes.
Taper /usr/bin/time calculV0
Exécuter calculV1 et calculV2 (sans /usr/bin/time). Pourquoi est-ce que ces deux programmes plantent ?

Question 2

Corriger calculV1.c et calculV2.c pour qu'ils ne plantent plus.
Taper /usr/bin/time calculV1
Taper /usr/bin/time calculV2
Calculer le gain de performances obtenu par rapport à celles de calculV0. Commenter.

NB : Quelques liens pour approfondir :

Exercice 9 : Arbre AVL

Un arbre AVL (arbre de Georgy Adelson-Velsky et Landis, nommé d'après ses inventeurs) est un arbre binaire de recherche qui s'auto-équilibre. Dans un arbre AVL, la hauteur de deux arbres enfant d'un nœud quelconque diffère d'au plus 1. Si à un moment, leur hauteur diffère de plus de un, un rééquilibrage est déclenché.
Cela garantit une vitesse de recherche dans l'arbre qui est en log2(n), n étant le nombre de nœuds.
Par exemple, l'arbre de la figure 1 est un arbre AVL, car, pour chaque nœud, la différence de hauteur entre le sous-arbre de gauche et le sous-arbre de droite est inférieure ou égale à 1.
Exemple d'arbre AVL
Figure1 : exemple d'abre AVL (exemple provenant d'ici)

En revanche, l'arbre de la figure 2 n'est pas un arbre AVL. En effet, pour le nœud 12, la hauteur du sous-arbre de gauche est 4, tandis que la hauteur du sous-arbre de droite est 2.
Exemple d'arbre non-AVL
Figure 2 : exemple d'arbre non-AVL (exemple provenant d'ici)





Dans cet exercice, vous allez coder un arbre AVL que nous utiliserons pour stocker des paires clé/valeur comme le font les classes Dictionnary de Java et std::map de la STL C++.

Remarque préliminaire

Vous devez veiller à ce que :
  1. votre code compile sans warning ;
  2. dans votre code, quand vous libérez une zone mémoire, vous mettez le pointeur sur cette zone mémoire à NULL.

Préparation

Se placer dans un répertoire de travail
tar   xvfz   arbreAVL.tgz
cd   ArbreAVL
mv   arbreAVL.c   arbreAVL_$USER.c
mv   testsUnitaires.c   testsUnitaires_$USER.c

Cet exercice s'appuie sur un environnement de tests unitaires appelé cmocka. Vous allez l'installer pour pouvoir vous en servir..
Téléchargez l'archive contenant la dernière version de cmoka (à l'heure où nous écrivons cet énoncé, cette archive s'appelle cmocka-1.1.1.tar.xz). Dans le terminal dans lequel vous avez effectué les opérations précédentes :
tar   xvfz   cmocka-*.tar.xz
mv   cmocka-*   cmocka
cd   cmocka
mkdir   build
cd   build
cmake   -DCMAKE_INSTALL_PREFIX=../install   -DCMAKE_BUILD_TYPE=Debug   ..
make
make install   # Ca y est : cmocka est installe
cd   ../..   # Vous etes revenu.e. dans le repertoire ou vous allez faire votre exercice
export   LD_LIBRARY_PATH=$LD_LIBRARY_PATH:cmocka/install/lib

Question 1 : Amélioration des tests unitaires

Cet exercice est fait selon la méthode TDD (Test Driven Development). L'odée est d'écrire complètement les tests de votre module dans testsUnitaires_usernameUnix.c, avant de passer au codage de votre module dans arbreAVL_usernameUnix.c.
Dans le fichier testsUnitaires_usernameUnix.c, en vous inspirant du code déjà existant, remplacez les lignes signalées par un TODO par un appel à assert_null() ou assert_int_equal() pour tester le cas où l'une des fonctions offertes par arbreAVL_usernameUnix.c est appelée avec un arbre AVL vide (i.e. le paramètre vaut NULL).
Dans votre terminal, tapez make run pour compiler votre code et l'exécuter. Sans surprise, tous les tests échouent.

NB :
  1. La compilation génère le warning "arbreAVL_usernameUnix.c:57:21: warning: ‘newNode’ defined but not used [-Wunused-function] static struct Node* newNode(const char *key)". Vu que vous n'avez pas encore commencé à coder arbreAVL_usernameUnix.c, ce warning est acceptable. Les warnings de compilation ne seront plus acceptés à partir de la question suivante.
  2. Si la commande make run affiche le message "./testsUnitaires: error while loading shared libraries: libcmocka.so.0: cannot open shared object file: No such file or directory", dans votre terminal, tapez la commande :
    export   LD_LIBRARY_PATH=$LD_LIBRARY_PATH:cmocka/install/lib

Question 2 : Codage des fonctions update_height(), nbNodes(), newNode(), insert() et release()

Dans arbreAVL_usernameUnix.c, complétez tous les TODO des fonctions updateHeight(), nbNodes(), newNode() et insert(). Ne vous occupez pas de release() pour l'instant.
NB : Ne vous préoccupez pas pour l'instant de construire un arbre équilibré (la fonction insert ne réalise donc pas, pour l'instant, d'auto-équilibrage) ou du stockage de la valeur associée à chaque clé. Aussi, votre fonction insert doit travailler de la manière suivante :
  • Si node vaut NULL, insert renvoie newNode(key)
  • Si key est plus petit que node->key, insert insère key dans une feuille la plus basse possible du sous-arbre de gauche de node.
    Pour ce faire, nous vous recommandons d'appeler récursivement insert. NB : la fonction insert() renvoie un struct node *. Aussi, n'oubliez pas de stocker le noeud renvoyé par insert() dans votre structure de données (ne vous contentez pas d'appeler insert() sans tenir compte de sa valeur de retour, dit autrement comme si c'était une procédure ; vous auriez alors probablement des bugs à cette question ou aux questions suivantes).
  • Si key est plus grand que node->key, insert insère key dans une feuille la plus basse possible du sous-arbre de droite de node.
    Pour ce faire, nous vous recommandons d'appeler récursivement insert. Même NB qu'au point précédent.
  • Si key est égal à node->key, insert renvoie simplement node sans rien faire d'autre (ainsi, il n'y jamais deux valeurs identiques dans l'arbre).
  • Puis, insert met à jour le champ height de node.
Une fois que vos fonctions sont codées, tapez make run. Corrigez vos fonctions jusqu'à ce que make run affiche.
[ RUN      ] test_insert_et_nbNode
[       OK ] test_insert_et_nbNode
Dit autrement, le test unitaire test_insert_et_nbNode est OK.

Exécutez maintenant valgrind ./testsUnitaires. Valgrind vous signale au minimum que vous avez des fuites mémoires. Complétez release() et éventuellement les autres fonctions codées jusqu'à présent pour que valgrind ./testsUnitaires ne vous signale aucun problème d'accès mémoire ou de fuite mémoire.
Ainsi, test_insert_et_nbNode et test_release sont OK.

Question 3 : Codage des fonctions getKeyHeight() et getKeyRank()

Codez getKeyHeight() jusqu'à ce que test_getKeyHeight soit OK. N'oubliez pas de vérifier avec valgrind que vous n'avez aucun soucis d'accès mémoire ou de fuite mémoire.
Codez getKeyRank() jusqu'à ce que  test_getKeyRank soit OK.

Question 4 : Mise à jour du code pour équilibrer l'arbre au moment de sa construction

Cette question a pour objectif de travailler à l'équilibrage de l'arbre au moment de sa construction.

Pour ce faire, une fois l'insertion réalisée, pour chacun des nœuds concernés par l'insertion, il faut calculer sa "balance", i.e. la différence de hauteur entre le sous-arbre gauche du nœud et son sous-arbre droit.
Si la balance d'un nœud est comprise entre -1 et +1, il n'y a rien à faire.
En revanche, si la balance d'un nœud est strictement inférieure  à -1 ou strictement supérieure à +1, il faut rééquilibrer l'arbre au niveau de ce nœud. 4 cas se présentent (à traiter dans la fonction insert() après le calcul de la balance). Dans la présentation de ces 4 cas, 3 nœuds jouent un rôle particulier :
  • z est le nœud le plus profond au niveau duquel la balance est strictement inférieure à -1 ou strictement supérieure à +1 ;
  • x est le nœud petit-enfant de z, dont l'arbre contient la clé qui vient d'être insérée ;
  • y est le nœud enfant de z, qui est parent de x.
1er cas - Cas Gauche-Gauche : Au niveau de z, l'arbre est déséquilibré à gauche. De plus, x est situé à gauche de y. Alors, pour rééquilibrer l'arbre, il suffit d'effectuer une rotation droite de z (cf. figure 3).
Rotation droite de z
Figure 3 : Traitement du cas Gauche-Gauche (figure provenant d'ici)

2ème cas - Cas Droite-Droite : Au niveau de z, l'arbre est déséquilibré à droite. De plus, x est situé à droite de y. Alors, pour rééquilibrer l'arbre, il suffit d'effectuer une rotation gauche de z (cf. figure 4).
Rotation gauche de z
Figure 4 : Traitement du cas Droite-Droite (figure provenant d'ici)

3ème cas - Cas Gauche-Droite : Au niveau de z, l'arbre est déséquilibré à gauche. De plus, x est situé à droite de y. Alors, pour rééquilibrer l'arbre, il suffit d'effectuer une rotation gauche de y, puis une rotation droite de z (cf. figure 5).
Rotation gauche de y, puis rotation droite de z
Figure 5 : Traitement du cas Gauche-Droite (figure provenant d'ici)

4ème cas - Cas Droite-Gauche : Au niveau de z, l'arbre est déséquilibré à droite. De plus, x est situé à gauche de y. Alors, pour rééquilibrer l'arbre, il suffit d'effectuer une rotation droite de y, puis une rotation gauche de z (cf. figure 6).
Rotation droite de y, puis rotation gauche de z
Figure 6 : Traitement du cas Droite-Gauche (figure provenant d'ici)

Gérer ces 4 cas pour ce nœud suffit à rééquilibrer l'arbre entier, car l'arbre retrouve sa hauteur d'origine.

Complétez arbreAVL_usernameUnix.c :
  1. codez la fonction static struct Node *rightRotate(struct Node *z) qui réalise le traitement présenté à la figure 3  et retourne la nouvelle racine du sous arbre (ici, y); par ailleurs, cette fonction modifie le champ height des nœuds y et z ;
  2. codez la fonction static struct Node *leftRotate(struct Node *z) qui réalise le traitement présenté à la figure 4  et retourne la nouvelle racine du sous-arbre (ici y); par ailleurs, cette fonction modifie le champ height des nœuds y et z ;
  3. ajoutez le calcul de la balance et le traitement des 4 cas à la fonction insert().
Puis, tapez make run. Corrrigez vos fonctions jusqu'à ce que :
  1. les tests test_equilibrageGaucheGauche, test_equilibrageDroiteDroite, test_equilibrageGaucheDroite et test_equilibrageDroiteGauche soient OK ;
  2. valgrind ./testsUnitaires ne vous signale aucun problème d'accès mémoire ou de fuite mémoire.

Question 5 : Stockage de paires clé/valeur dans l'arbre AVL

Jusqu'à présent, notre arbre AVL n'a servi qu'à stocker des clés. Cette question a pour objectif d'utiliser cet arbre pour y stocker des paires clé/valeur, et ainsi obtenir une structure de données de type dictionnaire (cf. classes Dictionnary de Java et std::map de la STL C++) nous permettant de retrouver la valeur associée à une clé avec une complexité en O(log(n)).

Complétez arbreAVL.h et arbreAVL_usernameUnix.c :
  1. ajoutez un champ char *value à la structure Node ;
  2. ajoutez un paramètre char *value à la fonction newNode et recopiez le dans le champ value du nœud créé à l'aide de ce paramètre ;
  3. ajoutez un paramètre char *value à la fonction insert et utilisez ce paramètre de manière adéquate dans cette fonction. En particulier, s'il existe déjà un nœud ayant pour clé key dans l'arbre, veillez à remplacer l'ancienne valeur du champ value de ce nœud à l'aide de ce paramètre ;
  4. Complétez la fonction getValue()
    NB : il est probable que l'algorithme de recherche du nœud dans getValue()ressemble à s'y méprendre à l'algorithme de recherche du nœud dans getKeyHeight(). Pour éviter une duplication de code (jamais saine dans un projet), créez une fonction qui centralise ce code et qui est appelée par getValue()et getKeyHeight().
Prenez en compte, dans les tests unitaires testsUnitaires_usernameUnix.c, le changement de la signature de la fonction insert() en tapant les commandes suivantes dans un terminal :
cp   testsUnitaires_$USER.c   /tmp   # pour sauvegarder votre fichier initial
sed   's@); //@,@g'   testsUnitaires_$USER.c > /tmp/toto.c
cp   /tmp/toto.c   testsUnitaires_$USER.c

Par précaution, vérifiez que les changements de ligne se sont bien passés en tapant
diff   testsUnitaires_$USER.c   /tmp/testsUnitaires_$USER.c   |   grep   -v   insert
Vous ne devez avoir que des lignes du style
38c38
---
54,58c54,58
---

dit autrement, que des lignes mentionnant des numéros de lignes (comme 38c38 ci-dessus) ou "---". Si ce n'est pas le cas, faites
cp   /tmp/testsUnitaires_$USER.c   .
afin de récupérer votre fichier initial. Puis, postez un message de SOS sur le forum.

Enfin, tapez make run. Corrrigez vos fonctions jusqu'à ce que :
  1. le test test_getValue soit OK ;
  2. valgrind ./testsUnitaires ne vous signale aucun problème d'accès mémoire ou de fuite mémoire.
Remarque finale : il existe d'autres algorithmes qu'AVL pour obtenir des arbres binaires équilibrés. Un autre algorithme célèbre est l'arbre bicolore ou arbre rouge-noir (en anglais : red-black tree). C'est d'ailleurs ce dernier qui est utilisé dans les classes Dictionnary de Java et std::map de la STL C++. Pour le langage C, http://libredblack.sourceforge.net/ en propose une implémentation utilisable pour tout type de données. N'hésitez pas à vous en servir plutôt que de réinventer la roue !

Livraison de votre travail sous Moodle

Quand vous aurez terminé votre exercice, si vous souhaitez faire remonter des remarques au correcteur de votre travail, créez un fichier readme.txt dans le répertoire où se trouve votre travail et notez dans ce fichier vos remarques.
Tapez ensuite la commande : make  gentgz
2 cas peuvent se présenter :
  • Cas 1 : Cette commande affiche le message "===>  OK, fichier arbreAVL_usernameUnix.tgz genere : vous pouvez le remonter dans Moodle."
    La génération du fichier d'archive arbreAVL_usernameUnix.tgz s'est bien passée. Il ne vous reste plus qu'à remonter ce fichier dans Moodle.
  • Cas 2 : cette commande affiche le message "make: ***  Aucune règle pour fabriquer la cible « arbreAVL_usernameUnix.c », nécessaire pour « gentgz ». Arrêt." (et/ou le même message concernant testsUnitaires_usernameUnix.c). Cela signifie que vous avez oublier de taper la commande cp  arbreAVL.c  arbreAVL_$USER.c (et/ou la commande cp concernant testsUnitaires.c) dans la phase préliminaire de cet exercice. Faites-le(s), puis tapez à nouveau make  gentgz
Page mise à jour le 22 mai 2017