Introduction aux systèmes d'exploitation Examen de 2 heures Tous documents autorisés (sauf PC) Nicolas Gibelin 30 novembre 2004 1 Synchronisation (6 Points - 50 Minutes) 1.1 Question (3 points) Soit deux processus cycliques PA et PB. A chaque itération PA et PB eectuent des calculs préliminaires respectivement prélima et prélimb. Puis PA a besoin des résultats de PrélimB pour eectuer un calcul CalA, tandis que PB a besoin des résultats de PrélimA pour eectuer un calcul CalB. Ils recommencent ensuite une nouvelle itération. 1.1.1? Question? Programmer les processus PA et PB avec les synchronisations nécessaires aux moyen de sémaphores, en spéciant soigneusement les sémaphores nécessaires, et leur initialisation. 1.1.2 Réponse Il s'agit d'un rendez-vous (ou barrière pour 2 processus). Un sémaphore privé (resp sema et semb), initialisé à 0, permet à chaque processus d'attendre l'autre. PA : repeat { PrélimA ; V(semB) ; P(semA) ; CalA ; PB : repeat { PrélimB ; V(semA) ; P(semB) ; CalB ; Nota : Tout processus qui boucle, réexécute Prélim donc détruit ses résultats précédents. Une seconde barrière est donc nécessaire en n de boucle. On peut utiliser les mêmes sémaphores car les processus restent "en phase" ils attendent ensemble la première barrière ou ensemble la seconde. PA : repeat { PrélimA ; V(semB) ; P(semA) ; CalA ; V(semB) ; P(semA) ; PB : repeat { PrélimB ; V(semA) ; P(semB) ; CalB ; V(semA) ; P(semB) ; Néanmoins la question n'était pas posée explicitement, donc aucune pénalisation et au contraire, bonus pour ceux qui y ont pensé. 1.2 Question (3 points) Le processus PA n'a que ces calculs à eectuer. Au contraire le processus PB a aussi un autre calcul AltB à eectuer. Si PA n'a pas terminé PrélimA, on souhaite que PB eectue une itération de AltB avant de retester si PA a terminé PrélimA. PB peut ainsi eectuer plusieurs fois AltB tant que PA n'a pas terminé PrélimA. On souhaite par contre qu'il eectue CalB quand c'est possible. 1.2.1? Question? Programmer cette nouvelle version des processus PA et PB avec les synchronisations nécessaires au moyen de sémaphores, en spéciant soigneusement les sémaphores et les variables auxiliaires (partagées ou pas) nécessaires. 1
1.2.2 Réponse Pour eectuer un autre calcul AltB, le processus PB ne doit donc pas se bloquer au moyen d'un sémaphore. A la place de semb, on utilise donc pour B une variable booléenne FiniA initialisée à faux et protégée par un sémaphore d'exclusion mutuelle Mutex, initialisé à 1. PA : repeat {PrélimA ; P(Mutex) ; FiniA := true ; V(Mutex) ; P(semA) ; CalA ; V(semB) ; P(semA) repeat { Pr\'elimB; V(semA); boucle:=true; While boucle do { P(Mutex); if FiniA then { boucle := false; FiniA V(Mutex) else { V(Mutex); AltB; CalB; V(semA); P(semB); := false; Nota : Tout processus qui boucle, réexécute Prélim donc détruit ses résultats précécents. Une seconde barrière est encore nécessaire. On reprend la méthode simple de la question précédente. 2 Unix (5 Points - 20 Minutes) Les questions de cette partie sont indépendantes. 2.1 Question (2 points) On considère le programme suivant, où le programme mon_prog() eectue un unique achage qui est celui de la valeur de son premier paramètre (son argv[1]) (le programme qui suit n'est pas mon_prog()) : int main(int argc, char *argv[]) { int pid; printf("debut application\n"); pid = fork(); if ( pid == 0 ) { printf("avant exec\n"); execl("mon_prog", "mon_prog", "Nom", "Prenom", "Adresse", NULL); printf("apres exec\n"); exit(0); wait(null); printf("fin application\n"); return 0; On suppose que l'instruction execl() ne provoque pas d'erreur. Donnez les achages eectués par chacun des processus engendrés lors de l'exécution de ce programme. 2
2.1.1 Réponse On a deux processus. Le procesus père ache successivement : Debut application / Fin application Le processus ls ache : Avant exec / Nom 2.2 Question (3 points) Pour le programme ci-dessous : Combien de processus sont-ils créés? Faites un schéma représentant la hiérarchie des processus en indiquant pour chacun le programme qu'il exécute (foo, bar ou baz). int main(void) { if (fork()!= 0) { if (fork()!= 0) { execl("foo", 0); execl("bar", 0); else { execl("bar", 0); else { fork(); if (fork()!= 0) exit(0); execl("foo"); execl("baz", 0); return 0; 2.2.1 Réponse L'execution du programme précédent crée 5 processus en plus du processus racine. La gure ci-dessous représente l'arbre d'exécution du programme : 2.3 Sleep (1 point) On considère le morceau de programme suivant : sleep(5); i++; 3
Un peu plus de 5 secondes après l'exécution de sleep(5), l'utilisateur (qui observe le déroulement de son programme à l'aide d'un debugger) constate que l'instruction i++ n'a toujours pas été exécutée. Quelles explications pouvez-vous lui donner? 2.3.1 Réponse Première raison : à l'échéance du timer, le processus est remis dans l'état prêt. Mais si d'autres processus plus prioritaires sont en attente, il n'est pas élu immédiatement. Il y a donc un temps d'attente supplémentaire, correspondant à l'exécution des processus plus prioritaires, avant que l'instruction i++ soit exécutée. Deuxième raison : sous Unix, la précision de la mesure du temps est celle du tick horloge soit en général 10ms. Le paramètre du sleep ne peut donc être mesuré qu'à 10ms près. 3 Ordonnancement de taches (4 Points - 30 Minutes) Soit un système en temps partagé géré selon la stratégie Round Robin (RR). On veut eectuer un équilibrage entre les tâches en gérant de manière dynamique la valeur du quantum de temps : si au moment d'allouer le processeur, il y a N processus prêts alors on donne au processus choisi (qui devient courant), un quantum Q = minimum(200 milli-secondes, 1 seconde / N). Pour simplier, on suppose que tous les processus sont et restent en mémoire. Le système doit comptabiliser la durée totale d'exécution de chaque tâche (mais pour simplier, il ne limite pas ces durées totales). La machine est munie d'un temporisateur ayant un registre de capacité supérieure à 1 seconde. On admet que les durées de traitement des exceptions sont négligeables. Le passage de l'état d'une tâche de courant à bloqué s'eectue sur appel système, et le passage de bloqué à prêt s'eectue sur interruption de n d'entrée/sortie. Lorsque le système reprend une tâche qui avait été bloquée pour une demande d'entrée-sortie, il lui redonne un quantum entier (dont la valeur est déterminée à ce moment). 3.1 Question (2 points) À l'instant t = 0, il y a 6 processus en le d'attente dans l'ordre A, B, C, D, E, F. Le système alloue le processeur. À l'instant t = 300ms, le processus courant demande une E/S qui se termine au temps 700ms. À l'instant t = 500ms, le processus courant se termine. Tracer un diagramme de Gantt de l'exécution jusqu'en t = 800ms. 3.1.1 Réponse En t=000 : N=6 processus A courant avec Q=min(200, 1000/6) = 167 ms En t=167 : N=6 processus B courant avec Q=167 ms En t=300 : B demande une E/S N=5 et B bloqué En t=300 : C courant avec Q=min(200, 1000/5) = 200 ms En t=500 : C se termine N=4 En t=500 : D courant avec Q = min(200, 1000/4) = 200 ms En t=700 : n E/S B B prêt et N=5 En t=700 : E courant avec Q=min(200, 1000/5) = 200 ms En t=800 : E est encore courant 3.2 Question (2 points) Indiquer les champs des descripteurs de tâche et les variables du système qui sont utiles (uniquement pour cette gestion du processeur, des quantas et des durées d'exécution). Préciser les actions du système : lors de la création d'une tâche (en particulier pour l'initialisation des variables du descriptif qui vous sont nécessaires). lors d'une n d'e/s. lors d'une interruption du temporisateur. lors d'un lancement ou d'une reprise de tâche. Pour ces actions, traiter essentiellement le cas génèral et signaler les cas particuliers éventuels en indiquant brièvement leur traitement. 4
3.2.1 Réponse Dans les descripteurs, il faut État, Compteur de temps CT. Pour le système : Repère du descripteur courant (nil si le processus est oisif), File d'attente FA des processus prêts ou du processus courant, nombre N de processus prêts ou courant, Q=valeur du quantum du processus courant. (On ne calcule Q qu'au moment de la reprise d'une tâche, si on voulait calculer Q à chaque modication de N, il faudrait une variable QC=valeur du quantum du processus courant et il faudrait éviter une division par 0). Initialisation : processeur oisif ; N = 0 ; FA = vide. Création de tâche T[i] : État[i] := Prêt ; CT[i] := 0 ; N := N+1 ; Mettre tâche T[i] en n de FA ; Si processeur oisif, alors reprendre une tâche sinon continuer processus courant ; Fin d'e/s pour T[i] : État[i] := Prêt ; N := N+1 ; Metttre tâche T[i] en n de FA ; Si processeur oisif, alors reprendre une tâche sinon continuer processus courant ; Interruption temporisateur : CT[i] := CT[i] + Q ; désarmer tempo ; passer à la tâche suivante (qui est même si elle est la seule de FA) ; Reprendre une tâche ; Reprise d'une tâche : Si FA = vide, alors processeur oisif (pas de calcul de Q donc pas de division par 0) sinon { calcul de Q : allouer le processeur à la première tâche T[i] pour un quantum Q ; État[i] := courant ; Rtempo := Q ; armer Tempo 4 Système de chier (4 points - 20 minutes) Un système de chiers utilise des blocs de 1024 octets. Les inodes comprennent (entre autres informations) : 6 pointeurs de blocs directs, 1 pointeur de bloc à simple indirection, et 1 pointeur de bloc à double indirection. Les adresses de blocs font 2 octets chacune. Chaque entrée de répertoire a une taille de 128 octets (2 octets pour le numéro de l'inode, et 126 pour le nom du chier). On désire explorer l'arborescence suivante. 1. Combien de blocs sont-ils nécessaires pour stocker chacun de ces chiers? Combien de blocs sont-ils nécessaires pour stocker chacun des répertoires? Détaillez le nombre d'inodes, de blocs d'index et de blocs de données. 2. On désire obtenir la liste des chiers dont la date de création est supérieure à une date donnée. Combien de lectures de blocs disque sont-elles nécessaires? 3. On désire obtenir la liste des chiers dont le contenu comprend le caractère 'x'. Combien de lectures de blocs disque sont-elles nécessaires? 4.1 Réponses 4.1.1 Rappels Dans le cas présent, la taille maximum d'un chier pouvant être stocké est : 6 + 1024/2 + (1024/2) 2 = 262662blocs, soit 262662 1024octets = 268965888octets. En résumé, les structures dénies pour ce système de chier permet de stocker des chiers de 256 Mo maximum. 5
4.1.2 Stockage des chiers passwd : 8944/1024 = 9blocs + 1 inode hosts : 122/1024 = 1blocs + 1 inode ls : 120144/1024 = 118blocs + 1 bloc d'indirection simple + 1 inode cp : 32768/1024 = 32blocs + 1 inode vi (cf gure 1) : 9482096/1024 = 9260blocs + 1 bloc d'ind simple + 1 bloc d'ind simple de niveau 2 + 18 blocs d'ind double + 1 inode vmunix : Fig. 1 Enregistrement disk du chier vi 4.1.3 Lecture des repertoire et des chiers sur disque Chargement du bloc d'inode referencant le rep racine : 1 bloc chargement du bloc contenant les inodes des sous rep du rep racine : 1 bloc lecture de ce bloc et recup des inodes de etc, bin, vmunix puis recuperation du bloc contenant les inodes du rep etc, puis bin : 2 blocs chaque inode recupérée contient les info de date... Dans l'exemple précédent, 4 accès blocs sont nécessaires. 4.1.4 Recherche dans un chier A vous d'essayer... 6