# TP 1 : Mémoire ## Exercice 1 >Soit le [programme](TP1/Exo1/adresses_virtuelles.c) suivant qui affiche les adresses virtuelles de certaines variables lors de l'exécution du processus. >En utilisant le (pseudo) fichier `/proc/pid/maps`, vérifiez à quel segment de pages ces adresses appartiennent. ```shell tom@Error404:/mnt/c/Users/naser/Documents/scr/TP1$ ./adresses_virtuelles ``` >mon pid est 20 ``` main = 0x564bfc70e169 &argc = 0x7ffd8f4ac78c &i = 0x7ffd8f4ac794 &j = 0x564bfc711fe0 t = 0x564bfc711040 m = 0x564bfd7e62a0 ``` ```shell tom@Error404:/mnt/c/Users/naser/Documents/scr/TP1$ cat /proc/20/maps ``` ``` 564bfc70d000-564bfc70e000 r--p 00000000 00:48 3659174697600603 /mnt/c/Users/naser/Documents/scr/TP1/adresses_virtuelles 564bfc70e000-564bfc70f000 r-xp 00001000 00:48 3659174697600603 /mnt/c/Users/naser/Documents/scr/TP1/adresses_virtuelles 564bfc70f000-564bfc710000 r--p 00002000 00:48 3659174697600603 /mnt/c/Users/naser/Documents/scr/TP1/adresses_virtuelles 564bfc710000-564bfc711000 r--p 00002000 00:48 3659174697600603 /mnt/c/Users/naser/Documents/scr/TP1/adresses_virtuelles 564bfc711000-564bfc712000 rw-p 00003000 00:48 3659174697600603 /mnt/c/Users/naser/Documents/scr/TP1/adresses_virtuelles 564bfd7e6000-564bfd807000 rw-p 00000000 00:00 0 [heap] 7f246a43d000-7f246a440000 rw-p 00000000 00:00 0 7f246a440000-7f246a466000 r--p 00000000 08:20 10783 /usr/lib/x86_64-linux-gnu/libc.so.6 7f246a466000-7f246a5bb000 r-xp 00026000 08:20 10783 /usr/lib/x86_64-linux-gnu/libc.so.6 7f246a5bb000-7f246a60e000 r--p 0017b000 08:20 10783 /usr/lib/x86_64-linux-gnu/libc.so.6 7f246a64e000-7f246a658000 r--p 00026000 08:20 10780 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7f246a658000-7f246a65a000 r--p 00030000 08:20 10780 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7f246a65a000-7f246a65c000 rw-p 00032000 08:20 10780 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 7ffd8f48e000-7ffd8f4af000 rw-p 00000000 00:00 0 [stack] 7ffd8f568000-7ffd8f56c000 r--p 00000000 00:00 0 [vvar] 7ffd8f56c000-7ffd8f56e000 r-xp 00000000 00:00 0 [vdso] ``` ``` main appartient au deuxieme segment de pages (564bfc70e000-564bfc70f000) &argc appartient au stack (7ffd8f48e000-7ffd8f4af000) &i appartient aussi au stack (7ffd8f48e000-7ffd8f4af000) &j appartient au heap (564bfd7e6000-564bfd807000) t appartient au heap (564bfd7e6000-564bfd807000) m appartient au heap (564bfd7e6000-564bfd807000) ``` ### Exercice 1 bis >L'interface (pseudo-fichier) `proc/pid/smaps` permet de voir la consommation mémoire d'un processus. On peut le formater avec la commande `pmap -X`. Le but de l'exercice est de voir ce qui se passe au niveau de la mémoire d'un processus suivant les différents mode d'allocation. Le programme `null.c` permet d'avoir un point de comparaison. Dans les cas suivants, vérifiez où se trouve la memoire correspondante : 1. Allocation statique [buf.c](TP1/Exo1/ex1bis/buf.c) ```shell tom@Error404:/mnt/c/Users/naser/Documents/scr/TP1/Exo1/ex1bis$ pmap -x 51 ``` ``` 51: ./buf Address Kbytes RSS Dirty Mode Mapping 000055a1b721c000 4 4 0 r---- buf 000055a1b721d000 4 4 0 r-x-- buf 000055a1b721e000 4 4 0 r---- buf 000055a1b721f000 4 4 4 r---- buf 000055a1b7220000 4 4 4 rw--- buf 000055a1b7221000 24576 16516 16516 rw--- [ anon ] 000055a1b986a000 132 4 4 rw--- [ anon ] 00007f52d26db000 12 8 8 rw--- [ anon ] 00007f52d26de000 152 152 0 r---- libc.so.6 00007f52d2704000 1364 860 0 r-x-- libc.so.6 00007f52d2859000 332 108 0 r---- libc.so.6 00007f52d28ac000 16 16 16 r---- libc.so.6 00007f52d28b0000 8 8 8 rw--- libc.so.6 00007f52d28b2000 52 20 20 rw--- [ anon ] 00007f52d28c4000 8 4 4 rw--- [ anon ] 00007f52d28c6000 4 4 0 r---- ld-linux-x86-64.so.2 00007f52d28c7000 148 148 0 r-x-- ld-linux-x86-64.so.2 00007f52d28ec000 40 36 0 r---- ld-linux-x86-64.so.2 00007f52d28f6000 8 8 8 r---- ld-linux-x86-64.so.2 00007f52d28f8000 8 8 8 rw--- ld-linux-x86-64.so.2 00007ffc3f565000 132 12 12 rw--- [ stack ] 00007ffc3f5a8000 16 0 0 r---- [ anon ] 00007ffc3f5ac000 8 4 0 r-x-- [ anon ] ---------------- ------- ------- ------- total kB 27036 17936 16612 ``` >L'allocation statique de `buf.c` se trouve à l'adresse 000055a1b7221000 et elle est de 24576 Kbytes 2. Allocation sur la pile [stack.c](TP1/Exo1/ex1bis/stack.c) ```shell tom@Error404:/mnt/c/Users/naser/Documents/scr/TP1/Exo1/ex1bis$ pmap -x 57 ``` ``` 57: ./stack Address Kbytes RSS Dirty Mode Mapping 000055c5f583a000 4 4 0 r---- stack 000055c5f583b000 4 4 0 r-x-- stack 000055c5f583c000 4 4 0 r---- stack 000055c5f583d000 4 4 4 r---- stack 000055c5f583e000 4 4 4 rw--- stack 000055c5f5acb000 132 4 4 rw--- [ anon ] 00007f019bcba000 12 8 8 rw--- [ anon ] 00007f019bcbd000 152 152 0 r---- libc.so.6 00007f019bce3000 1364 804 0 r-x-- libc.so.6 00007f019be38000 332 112 0 r---- libc.so.6 00007f019be8b000 16 16 16 r---- libc.so.6 00007f019be8f000 8 8 8 rw--- libc.so.6 00007f019be91000 52 20 20 rw--- [ anon ] 00007f019bea3000 8 4 4 rw--- [ anon ] 00007f019bea5000 4 4 0 r---- ld-linux-x86-64.so.2 00007f019bea6000 148 148 0 r-x-- ld-linux-x86-64.so.2 00007f019becb000 40 36 0 r---- ld-linux-x86-64.so.2 00007f019bed5000 8 8 8 r---- ld-linux-x86-64.so.2 00007f019bed7000 8 8 8 rw--- ld-linux-x86-64.so.2 00007ffdebac4000 132 36 36 rw--- [ stack ] 00007ffdebbef000 16 0 0 r---- [ anon ] 00007ffdebbf3000 8 4 0 r-x-- [ anon ] ---------------- ------- ------- ------- total kB 2460 1392 120 ``` >L'allocation sur la pile de `stack.c` se trouve à l'adresse 00007ffdebac4000 et elle est de 132 Kbytes 3. Allocation sur le tas [heap.c](TP1/Exo1/ex1bis/heap.c) ```shell tom@Error404:/mnt/c/Users/naser/Documents/scr/TP1/Exo1/ex1bis$ pmap -x 59 ``` ``` 59: ./heap Address Kbytes RSS Dirty Mode Mapping 000055ac09aae000 4 4 0 r---- heap 000055ac09aaf000 4 4 0 r-x-- heap 000055ac09ab0000 4 4 0 r---- heap 000055ac09ab1000 4 4 4 r---- heap 000055ac09ab2000 4 4 4 rw--- heap 000055ac0a4c2000 50028 49924 49924 rw--- [ anon ] 00007f1e58321000 12 8 8 rw--- [ anon ] 00007f1e58324000 152 152 0 r---- libc.so.6 00007f1e5834a000 1364 844 0 r-x-- libc.so.6 00007f1e5849f000 332 64 0 r---- libc.so.6 00007f1e584f2000 16 16 16 r---- libc.so.6 00007f1e584f6000 8 8 8 rw--- libc.so.6 00007f1e584f8000 52 20 20 rw--- [ anon ] 00007f1e5850a000 8 4 4 rw--- [ anon ] 00007f1e5850c000 4 4 0 r---- ld-linux-x86-64.so.2 00007f1e5850d000 148 148 0 r-x-- ld-linux-x86-64.so.2 00007f1e58532000 40 36 0 r---- ld-linux-x86-64.so.2 00007f1e5853c000 8 8 8 r---- ld-linux-x86-64.so.2 00007f1e5853e000 8 8 8 rw--- ld-linux-x86-64.so.2 00007ffe0d57b000 132 12 12 rw--- [ stack ] 00007ffe0d5fa000 16 0 0 r---- [ anon ] 00007ffe0d5fe000 8 4 0 r-x-- [ anon ] ---------------- ------- ------- ------- total kB 52356 51280 50016 ``` >L'allocation sur le tas de `heap.c` se trouve à l'adresse 000055ac0a4c2000 et elle est de 50028 Kbytes 4. Allocation (gande quantité) sur le tas [huge.c](TP1/Exo1/ex1bis/huge.c) ```shell tom@Error404:/mnt/c/Users/naser/Documents/scr/TP1/Exo1/ex1bis$ pmap -x 61 ``` ``` 61: ./huge Address Kbytes RSS Dirty Mode Mapping 000055fdf6259000 4 4 0 r---- huge 000055fdf625a000 4 4 0 r-x-- huge 000055fdf625b000 4 4 0 r---- huge 000055fdf625c000 4 4 4 r---- huge 000055fdf625d000 4 4 4 rw--- huge 000055fdf6cef000 132 100 100 rw--- [ anon ] 00007f5bb5f2e000 272 268 268 rw--- [ anon ] 00007f5bb5f72000 152 152 0 r---- libc.so.6 00007f5bb5f98000 1364 852 0 r-x-- libc.so.6 00007f5bb60ed000 332 64 0 r---- libc.so.6 00007f5bb6140000 16 16 16 r---- libc.so.6 00007f5bb6144000 8 8 8 rw--- libc.so.6 00007f5bb6146000 52 20 20 rw--- [ anon ] 00007f5bb6158000 8 4 4 rw--- [ anon ] 00007f5bb615a000 4 4 0 r---- ld-linux-x86-64.so.2 00007f5bb615b000 148 148 0 r-x-- ld-linux-x86-64.so.2 00007f5bb6180000 40 36 0 r---- ld-linux-x86-64.so.2 00007f5bb618a000 8 8 8 r---- ld-linux-x86-64.so.2 00007f5bb618c000 8 8 8 rw--- ld-linux-x86-64.so.2 00007fffa6cae000 132 16 16 rw--- [ stack ] 00007fffa6cf2000 16 0 0 r---- [ anon ] 00007fffa6cf6000 8 4 0 r-x-- [ anon ] ---------------- ------- ------- ------- total kB 2720 1728 456 ``` >L'allocation (gande quantité) sur le tas de `huge.c` se trouve à l'adresse 00007f5bb5f2e000 et elle est de 272 Kbytes 5. Allocation sur le mapping [mmap.c](TP1/Exo1/ex1bis/mmap.c) ```shell tom@Error404:/mnt/c/Users/naser/Documents/scr/TP1/Exo1/ex1bis$ pmap -x 64 ``` ``` 64: ./mmap Address Kbytes RSS Dirty Mode Mapping 000055c9d1054000 4 4 0 r---- mmap 000055c9d1055000 4 4 0 r-x-- mmap 000055c9d1056000 4 4 0 r---- mmap 000055c9d1057000 4 4 4 r---- mmap 000055c9d1058000 4 4 4 rw--- mmap 000055c9d2214000 132 4 4 rw--- [ anon ] 00007fad683c0000 256 128 128 rw--- 256k 00007fad68400000 64 64 64 rw-s- zero (deleted) 00007fad68410000 44 40 40 rw--- [ anon ] 00007fad6841b000 152 152 0 r---- libc.so.6 00007fad68441000 1364 812 0 r-x-- libc.so.6 00007fad68596000 332 120 0 r---- libc.so.6 00007fad685e9000 16 16 16 r---- libc.so.6 00007fad685ed000 8 8 8 rw--- libc.so.6 00007fad685ef000 52 20 20 rw--- [ anon ] 00007fad685fd000 24 4 4 rw--- [ anon ] 00007fad68603000 4 4 0 r---- ld-linux-x86-64.so.2 00007fad68604000 148 148 0 r-x-- ld-linux-x86-64.so.2 00007fad68629000 40 36 0 r---- ld-linux-x86-64.so.2 00007fad68633000 8 8 8 r---- ld-linux-x86-64.so.2 00007fad68635000 8 8 8 rw--- ld-linux-x86-64.so.2 00007ffdc2360000 132 16 16 rw--- [ stack ] 00007ffdc2384000 16 0 0 r---- [ anon ] 00007ffdc2388000 8 4 0 r-x-- [ anon ] ---------------- ------- ------- ------- total kB 2828 1612 324 ``` >L'allocation sur le mapping de `mmap.c` se trouve à l'adresse 00007fad683c0000 et elle est de 256 Kbytes ## Exercice 2 >Soit le [programme](TP1/Exo2/bss_data.c) suivant. 1. Compilez le programme. Avec la commande `size`, regardez les différents segments du programme. Où se trouve le tableau `t` ? Augmentez la valeur de N. La taille de l'exécutable a-t-elle changé ? pourquoi ? >Le tableau `t` se trouve dans le segment `.bss` et la taille de l'exécutable n'a pas changé car le tableau `t` n'est pas initialisé. 2. Recommencez avec la version 2. Expliquez. >Le tableau `t` se trouve dans le segment `.data` et la taille de l'exécutable a changé car le tableau `t` est initialisé. ## Exercice 3 >Soit le [programme](TP1/Exo3/ij_ji.c) suivant. >Le temps d'éxecution de ce programme est-il différent pour les deux versions ? Pourquoi ? >Le temps d'éxecution de ce programme est différent pour les deux versions car dans la première version, les éléments du tableau sont accédés de manière séquentielle, alors que dans la deuxième version, le cache est rempli car on lit les éléments du tableau par colonne, donc il y a plus de cache miss vu qu'on fait des incréments bien plus grands que dans la première version. ## Exercice 4 >Le programme [sum_array.c](TP1/Exo4/sum_array.c) fait la somme des éléments d'un tableau en accédant aux éléments séquentiellement (`-c` croissant, `-d` décroissant) ou de manière aléatoire (`-a`). Testez en faisant varier la taille du tableau. Expliquez. >La raison de pourquoi la manière aléatoire est plus lente que la manière croissante ou décroissante (qui sont aussi rapide l'une que l'autre), est que la manière aléatoire fait plus de cache miss que les deux autres, car elle accède aux éléments du tableau de manière aléatoire, donc il y a plus de chance que l'élément ne soit pas dans le cache, alors que dans les deux autres, les éléments sont accédés de manière séquentielle, donc il y a plus de chance que l'élément soit dans le cache. Ce qui force la lecture de la RAM. ## Exercice 5 >Ecrire une fonction ```c void hexdump(void * ptr,size_t size); ``` >qui affiche sur la sortie standard le contenu de la mémoire `[ptr,ptr+size[` au format : ``` XXXXXXXX BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB BB |CCCCCCCCCCCCCCCC| ``` >(comme la commande shell) - `XXXXXXXXX` représente l'adresse du premier octet de la ligne - `BB` la valeur hexadécimale de chaque octet - `|CCCCCCCCCCCCCCCC|` la correspondance ascii de chaque octet (. si non affichable) >Est-ce conforme à ce que l'on a vu en cours concernant l'alignement en mémoire ? >L'alignement en mémoire est conforme à ce que l'on a vu en cours car les adresses sont alignées sur 16 octets. # TP 2 : Fichiers ## Exercice 1 >Écrire deux versions d'un programme qui copie "octet par octet" un fichier source dans un fichier destination : `./copy infile outfile` >en utilisant : 1. les appels systèmes linux : `open`, `read`, `write`, `close`. 2. les fonctions E/S standards (sdtio) : `fopen`, `fread`, `fwrite`, `fclose`. > Testez vos deux programmes sur un fichier volumineux (utilisez la commande `dd` le pseudo fichier `dev/urandom` pour générer un contenu aléatoire) et expliquez pourquoi le deuxième programme est beaucoup plus rapide que le premier. >Remarque: - pour être dans les mêmes conditions, il faut être sur que le fichier à copier n'est pas en cache en mémoire. Générez un nouveau fichier de même taille, ou utilisez le programme [fadvise.c](TP2/Exo1/fadvise.c) qui permet de vider les blocs du cache pour un fichier quelconque. - utilisez strace pour tracer les appels systèmes de vos deux programmes. >Le deuxième programme est beaucoup plus rapide que le premier car il utilise des buffers, alors que le premier programme lit et écrit octet par octet et fait plus d'appels systèmes. ## Exercice 2 >Écrire un programme qui copie un fichier en utilisant `mmap`. ## Exercice 3 >Compilez et exécutez Le programme [coherence.c](TP2/Exo3/coherence.c). Qu'est ce que cela montre ? >Ce programme montre que quand nous lisons avec stdio, le contenu du fichier est mis en cache, et quand nous lisons avec read, il n'y a pas de cache, c'est pourquoi quand nous lisons avec stdio nous avons l'ancienne version du fichier, et quand nous lisons avec read nous avons la nouvelle version du fichier. ## Exercice 4 >Le but est d'écrire en C un programme qui efface un fichier du disque de telle manière que le contenu effacé ne soit pas récupérable. Pour des raisons physiques, on procédera de la manière suivante : - Si l'inode correspondant au fichier à effacer à plusieurs références, on efface juste l'entrée du répertoire correspondant. - Sinon, on réécrit les blocs de données : - une première passe avec `0xff` pour tous les octets. - une deuxième passe avec des valeurs aléatoires (on utilisera le pseudo-fichier `/dev/urandom`) - enfin, avant d'effacer le fichier, on le renomera de manière aléatoire. >Toutes les E/S devront utilisées un cache. # TP 3 : Processus ## Exercice 1 > Compilez et exécutez [ex1-stdio](TP3/Exo1/ex1-stdio.c) et [ex1-syscall](TP3/Exo1/ex1-syscall.c). Expliquez. > Avec `stdio`, le printf écrit "NON" dans le cache, c'est pourquoi lors de l'utilisation du `fork` le printf écrit "NON" deux fois, alors qu'avec `syscall`, le write écrit "NON" directement dans la sortie standard, c'est pourquoi lors de l'utilisation du `fork` le printf écrit "NON" une seule fois. ## Exercice 2 > Compilez et exécutez [fork_and_fd1.c](TP3/Exo2/fork_and_fd1.c) et [fork_and_fd2.c](TP3/Exo2/fork_and_fd2.c). Expliquez. > Pour le premier programme, le `fork` est fait après l'ouverture du fichier, donc le père et le fils se partagent le même descripteur de fichier, si l'un écrit, l'offset va se mettre à jour pour que les deux écrivent à la suite. Pour le deuxième programme, le `fork` est fait avant l'ouverture du fichier, donc le père et le fils ont deux descripteurs de fichier différents, l'offset n'est pas partagé donc le contenu du fichier sera celui du dernier à écrire. ## Exercice 3 > Que fait le programme [copy1byte.c](TP3/Exo3/copy1byte.c) ? Décommentez la ligne du `fork`. Expliquer ce qui se passe. > Le programme [copy1byte.c](TP3/Exo3/copy1byte.c) copie le fichier `source` dans le fichier `destination` en utilisant des appels systèmes. Quand on décommente la ligne du `fork`, ce dernier est effectué après l'ouverture du fichier, donc le père et le fils ont deux descripteurs de fichier différents, l'offset n'est pas partagé donc le contenu du fichier sera celui du dernier à écrire. ## Exercice 4 > Ecrire un programme qui crée un processus fils. >Dans le fils : - imprimer le retour de fork(), getpid(), getppid(). - bloquer 4 secondes (sleep). - se terminer avec exit(2). > Dans le père : - imprimer le retour de fork(), getpid(), getppid(). - attendre la fin de son fils (wait()), et imprimer son code de retour. - afficher alors la liste de tous les processus actifs (execl() avec ps -ef). ## Exercice 5 > Le but de l'exercice est de détecter la présence d'un zéro dans un tableau `unsigned char` de taille `SIZE` en découpant le travail entre plusieurs processus. > Combien y-at-il de 0 au plus dans le tableau ? Complétez la fonction `search`, et testez. 1. Première version. Modifiez le programme pour que le processus crée un fils. Le fils et le père cherche chacun le zéro dans une moitié du tableau. Le fils communique le résultat à son père. Celui-ci, à l'aide de son propre travail, donnera la réponse. 2. Deuxième version. Votre programme accepte sur la ligne de commande un entier n entre 1 et 100. Le programme crée n fils qui cherche chacun dans une partie du tableau. Le père attend la fin de chacun de ses fils, récupère leur résultat et affiche la réponse. 3. Troisième version. On améliore la version précédente. Lorsque qu'un fils trouve le 0 dans le tableau, et que le père en est averti, faites en sorte que les autres fils vivants se terminent. On utilisera la primitive `kill()` qui perment d'envoyer le signal de terminaison (`SIGTERM`) à tout un groupe. ## Exercice 6 > Exécutez le programme [session.c](TP3/Exo6/session.c) et interprétez avec `ps` les informations `pid,ppid,pgid,sess,tpgid` des processus créés. ```shell tom@Error404:/mnt/c/Users/naser/Documents/scr/r305_dm/TP3/Exo6$ ./session session = 9 session = 8442 ``` ```shell tom@Error404:/mnt/c/Users/naser/Documents/scr/r305_dm/TP3/Exo6$ ps -o pid,ppid,pgid,sess,tpgid PID PPID PGID SESS TPGID 103 102 103 103 8443 8443 103 8443 103 8443 ``` > On peut voir qu'on a bien créé un processus fils, mais vu qu'on est pas leader de session on ne peut pas créer de nouvelle session, c'est pourquoi `sess` et `tpgid` sont les mêmes que le père. ## Exercice 7 > Écrire un programme qui pour `n>0` donné sur la ligne de commande, engendre l'arbre généalogique : ``` 0| |\ 1 | \ | |\ 2 | | \ | | |\ ... | | | \ | | | |\ n x x x x x ``` > Chaque processus choisit un nombre au hasard entre 0 et 127. Le processus 0 affichera la plus grande valeur choisie par tous les processus. > Remarque : on pourra modifier la séquence de nombres aléatoires en utilisant srand dans chaque processus créé. (pourquoi ?) # TP 4 : Signaux ## Exercice 1 > Pour calculer le nombre pi, on utilise la méthode de Monte-Carlo. On tire aléatoirement des couples (x,y) de nombres de [0,1] x [0,1]. La probabilité qu'il tombe dans le disque de rayon 1 est exactement de pi/4. On procède à plusieurs tirages pour estimer la probilité correspondante. > En utilisant les signaux, mettre en place : - avec `SIGALRM`, toutes les 5 secondes, l'affichage de la valeur pi en cours et le nombre de tirs effectués. - avec `SIGINT` l'arrêt du programme (après la demande d'une confirmation), avec l'affichage du temps écoulé depuis son lancement, quand on fait `ctrl+C` au terminal. - avec `SIGQUIT` la réinitialisation du calcul avec ctrl+\ depuis le terminal. (faites en sorte que toutes les valeurs restent cohérentes) > Dans chaque handler, les 2 autres signaux seront bloqués.