Exercice Corrigé Gestion Des Processus Linux – Partie 4

Les exercices pratiques sur la gestion des processus linux comportent des exercices théoriques et pratiques sur les concepts fondamentaux de la gestion des processus, y compris la création, la planification, et la terminaison des processus. Vous aurez l’occasion d’explorer des commandes essentiels tels que ps et top pour surveiller les processus en temps réel, ainsi que d’utiliser des commandes comme kill pour gérer les processus.

De plus, des exercices aborderont la gestion de la mémoire, l’utilisation des identifiants de processus (PID), ainsi que l’interaction entre les processus via des signaux. Les travaux pratiques incluront des scénarios de simulation où vous pouvez créerer des scripts pour automatiser la gestion des processus et résoudre des problèmes courants liés aux processus orphelins et aux zombies.

L’objectif est de fournir une compréhension approfondie de la manière dont le système d’exploitation Linux gère les processus, ainsi que des compétences pratiques pour gérer efficacement les processus dans un environnement réel.

 
 

Exercice 1: Question générale sur la gestion des processus Linux

1.1) Considérez les événements suivants qui se produisent lors d’un changement de contexte du processus P (mode utilisateur) au processus Q (mode utilisateur), déclenché par une interruption de temporisation qui s’est produite lorsque P était en cours d’exécution, dans un système d’exploitation de type Unix. Classez les événements par ordre chronologique, du plus ancien au plus récent.

A Le compteur de programme de l’unité centrale passe de l’espace d’adressage du noyau de P à l’espace d’adressage du noyau de Q.

B L’unité centrale exécutant le processus P passe du mode utilisateur au mode noyau.

C Le pointeur de pile de l’unité centrale passe de la pile du noyau de P à la pile du noyau de Q.

D Le compteur de programme de l’unité centrale passe de l’espace d’adressage du noyau de Q à l’espace d’adressage de l’utilisateur de Q.

D Le code du planificateur du système d’exploitation est invoqué.

B, E, A, C, D

Pour classer ces événements par ordre chronologique lors d’un changement de contexte entre les processus P et Q, voici l’ordre:

  • (B) L’unité centrale exécutant le processus P passe du mode utilisateur au mode noyau.
  • (E) Le code du planificateur du système d’exploitation est invoqué.
  • (A) Le compteur de programme de l’unité centrale passe de l’espace d’adressage du noyau de P à l’espace d’adressage du noyau de Q.
  • (C) Le pointeur de pile de l’unité centrale passe de la pile du noyau de P à la pile du noyau de Q.
  • (D) Le compteur de programme de l’unité centrale passe de l’espace d’adressage du noyau de Q à l’espace d’adressage de l’utilisateur de Q.

 

1.2) Considérons un système avec deux processus P et Q, exécutant un système d’exploitation de type Unix. Considérons les événements suivants qui peuvent se produire lorsque le système d’exploitation exécute simultanément P et Q, tout en gérant les interruptions.

(A) Le compteur de programme de l’unité centrale passe du code du noyau en mode noyau du processus P au code du noyau en mode noyau du processus Q.
(B) Le pointeur de pile de l’unité centrale passe de la pile du noyau de P à la pile du noyau de Q.
(C) L’unité centrale exécutant le processus P passe du mode utilisateur de P au mode noyau de P.
(D) L’unité centrale exécutant le processus P passe du mode noyau de P au mode utilisateur de P.
(E) L’unité centrale exécutant le processus Q passe du mode noyau de Q au mode utilisateur de Q.
(F) Le code de gestion des interruptions du système d’exploitation est invoqué.
(G) Le code du planificateur du système d’exploitation est invoqué.

Pour chacun des deux scénarios ci-dessous, indiquez l’ordre chronologique dans lequel les événements ci-dessus se produisent. Notez que tous les événements ne doivent pas nécessairement se produire dans chaque question.

(a) Une interruption de temporisation se produit pendant l’exécution de P. Après avoir traité l’interruption, le code de l’ordonnanceur du système d’exploitation est appelé. Après avoir traité l’interruption, le planificateur du système d’exploitation décide de revenir au processus P.
(b) Une interruption de temporisation se produit lorsque P est en cours d’exécution. Après avoir traité l’interruption, le planificateur du système d’exploitation décide de passer au processus Q, et le système se retrouve dans le mode utilisateur de Q.

Voici l’ordre chronologique des événements pour chaque scénario:

(a) Une interruption de temporisation se produit pendant l’exécution de P. Après avoir traité l’interruption, le code de l’ordonnanceur du système d’exploitation est appelé. Après avoir traité l’interruption, le planificateur du système d’exploitation décide de revenir au processus P.

  • (C) L’unité centrale exécutant le processus P passe du mode utilisateur de P au mode noyau de P.
  • (F) Le code de gestion des interruptions du système d’exploitation est invoqué.
  • (G) Le code du planificateur du système d’exploitation est invoqué.
  • (D) L’unité centrale exécutant le processus P passe du mode noyau de P au mode utilisateur de P.

(b) Une interruption de temporisation se produit lorsque P est en cours d’exécution. Après avoir traité l’interruption, le planificateur du système d’exploitation décide de passer au processus Q, et le système se retrouve dans le mode utilisateur de Q.

  • (C) L’unité centrale exécutant le processus P passe du mode utilisateur de P au mode noyau de P.
  • (F) Le code de gestion des interruptions du système d’exploitation est invoqué.
  • (G) Le code du planificateur du système d’exploitation est invoqué.
  • (A) Le compteur de programme de l’unité centrale passe du code du noyau en mode noyau du processus P au code du noyau en mode noyau du processus Q.
  • (B) Le pointeur de pile de l’unité centrale passe de la pile du noyau de P à la pile du noyau de Q.
  • (E) L’unité centrale exécutant le processus Q passe du mode noyau de Q au mode utilisateur de Q.

Ces ordres décrivent le déroulement typique des événements dans un système d’exploitation de type Unix lors de la gestion des interruptions et des changements de contexte.

1.3) Considérons les trois processus suivants, qui arrivent dans un système aux moments spécifiés, ainsi que la durée de leurs rafales de CPU. Le processus P1 arrive à l’instant t=0 et dispose d’une charge d’unité centrale de 10 unités de temps. Le processus P2 arrive à l’instant t=2 et dispose d’une durée d’utilisation de l’unité centrale de 2 unités. Le processus P3 arrive à l’instant t=3 et dispose d’un temps d’utilisation du processeur de 3 unités. Supposons que les processus ne s’exécutent qu’une seule fois pendant la durée de leur rafale de CPU et qu’ils se terminent immédiatement. Calculez le temps d’achèvement des trois processus pour chacune des politiques d’ordonnancement suivantes. Pour chaque politique, vous devez indiquer le temps d’achèvement des trois processus P1, P2 et P3. Supposez qu’il n’y a pas d’autres processus dans la file d’attente de l’ordonnanceur. Pour les politiques préemptives, supposez qu’un processus en cours peut être immédiatement préempté dès l’arrivée du nouveau processus (si la politique décide de préempter).

(a) First Come First Serve (« Premier arrivé, premier servi »)
(b) Shortest Job First (SJF) (« Plus Court Job d’abord ») (non préemptif)
(c) Shortest Remaining Time First (« plus court temps restant en premier ») (préemptif)
(d) Round robin (préemptif) avec une tranche de temps de (au moins) 5 unités par processus.

Voici les temps d’achèvement des processus P1, P2 et P3 pour chacune des politiques d’ordonnancement spécifiées :

(a) First Come First Serve (FCFS)
  • P1 arrive à t=0 et s’exécute pendant 10 unités. Il termine à t=10.
  • P2 arrive à t=2, mais attend que P1 se termine. Il commence à t=10 et termine à t=12.
  • P3 arrive à t=3, mais attend aussi. Il commence à t=12 et termine à t=15.

Temps d’achèvement :

  • P1 : 10
  • P2 : 12
  • P3 : 15

(b) Shortest Job First (SJF) (non préemptif)
  • P1 arrive à t=0 (10 unités), P2 à t=2 (2 unités), et P3 à t=3 (3 unités).
  • P2 est le plus court, donc il s’exécute en premier. P2 commence à t=2 et termine à t=4.
  • Ensuite, P3 est le suivant. P3 commence à t=4 et termine à t=7.
  • Enfin, P1 commence à t=7 et termine à t=17.

Temps d’achèvement :

  • P1 : 17
  • P2 : 4
  • P3 : 7

(c) Shortest Remaining Time First (SRTF) (préemptif)
  • À t=0, P1 est le seul (10 unités).
  • À t=2, P2 arrive et a un temps restant plus court, donc P1 est préempté. P2 s’exécute de t=2 à t=4.
  • À t=3, P3 arrive. À t=4, P2 termine.
  • P3 a un temps restant de 3 unités, et P1 a 8 unités restantes. P3 s’exécute de t=4 à t=7.
  • P1 reprend et termine à t=17.

Temps d’achèvement :

  • P1 : 17
  • P2 : 4
  • P3 : 7

(d) Round Robin (préemptif) avec tranche de temps de 5 unités
  • À t=0, P1 commence (10 unités) et s’exécute de t=0 à t=5.
  • P1 a encore 5 unités restantes et est préempté à t=5. P2 s’exécute de t=5 à t=7 (2 unités), et termine.
  • P3 commence à t=7 et s’exécute de t=7 à t=12 (5 unités). Il a 1 unité restante.
  • P1 reprend et termine à t=17.

Temps d’achèvement :

  • P1 : 17
  • P2 : 7
  • P3 : 13

Résumé des temps d’achèvement :

  • FCFS : P1=10, P2=12, P3=15
  • SJF : P1=17, P2=4, P3=7
  • SRTF : P1=17, P2=4, P3=7
  • Round Robin : P1=17, P2=7, P3=13

1.4) Considérons un système avec un seul cœur de CPU et trois processus A, B, C. Le processus A arrive à t = 0, et s’exécute sur le CPU pendant 10 unités de temps avant de se terminer. Le processus B arrive à t = 6 et nécessite un temps initial de 3 unités sur le CPU, après quoi il se bloque pour effectuer des E/S pendant 3 unités de temps. Après avoir quitté l’attente d’E/S, il s’exécute pendant 5 unités supplémentaires avant de se terminer. Le processus C arrive à t = 8 et s’exécute pendant 2 unités de temps sur l’unité centrale avant de se terminer. Pour chacune des politiques d’ordonnancement ci-dessous, calculez le temps d’achèvement de chacun des trois processus. Rappelez-vous que seule la taille du cycle d’utilisation de l’unité centrale (à l’exclusion du temps d’attente des entrées-sorties) est considérée comme la « taille du job » dans ces ordonnanceurs.

(a) First Come First Serve (« Premier arrivé, premier servi ») (non préemptif).
(b) Shortest Job First (SJF) (« Plus Court Job d’abord ») (non préemptif)
(c) Shortest Remaining Time First (« plus court temps restant en premier ») (préemptif)

Voici l’analyse des temps d’achèvement des processus A, B et C pour chaque politique d’ordonnancement :

(a) First Come First Serve (FCFS) (non préemptif)
  1. A arrive à t=0 et s’exécute pendant 10 unités (t=10).
  2. B arrive à t=6, mais attend que A se termine. B commence à t=10 et s’exécute pendant 3 unités (t=13).
  3. B se bloque pour E/S pendant 3 unités (t=13 à t=16).
  4. À t=16, B reprend et s’exécute pendant 5 unités (t=21).
  5. C arrive à t=8, mais attend jusqu’à t=21. C s’exécute ensuite et termine à t=23.

Temps d’achèvement :

  • A = 10
  • B = 21
  • C = 23

(b) Shortest Job First (SJF) (non préemptif)
  1. A arrive à t=0 et s’exécute pendant 10 unités (t=10).
  2. B arrive à t=6, mais ne peut pas commencer tant que A ne se termine pas. Après A, B commence à t=10 et s’exécute pendant 3 unités (t=13).
  3. B se bloque pour E/S de t=13 à t=16.
  4. C arrive à t=8 et attend jusqu’à t=16. C s’exécute ensuite pendant 2 unités (t=18).
  5. B redémarre à t=16 et termine après 5 unités (t=23).

Temps d’achèvement :

  • A = 10
  • B = 23
  • C = 18

(c) Shortest Remaining Time First (SRTF) (préemptif)
  1. A commence à t=0 et s’exécute jusqu’à t=6 (6 unités).
  2. B arrive à t=6 et commence à s’exécuter pendant 3 unités (t=9).
  3. C arrive à t=8, mais est préempté par B, qui a un temps restant plus court. C s’exécute ensuite à t=9 et termine à t=11.
  4. A reprend son exécution jusqu’à t=15 (fin de A).
  5. B reprend à t=15, ayant alors 3 unités restantes, et termine à t=20.

Temps d’achèvement :

  • A = 15
  • B = 20
  • C = 11

Résumé des temps d’achèvement :

  • FCFS : A=10, B=21, C=23
  • SJF : A=10, B=23, C=18
  • SRTF : A=15, B=20, C=11

(Notez que les valeurs que vous avez fournies pour SJF et SRTF contiennent des incohérences par rapport à l’analyse.)

1.5) Considérez les différents mécanismes et techniques d’ordonnancement de l’unité centrale utilisés dans les ordonnanceurs modernes.

(a) Décrivez une technique par laquelle un ordonnanceur peut donner une priorité plus élevée aux processus liés aux E/S avec de courtes durées d’utilisation du CPU qu’aux processus liés au CPU avec de plus longues durées d’utilisation du CPU, sans que l’utilisateur n’ait à spécifier explicitement les priorités ou les durées d’utilisation du CPU à l’ordonnanceur.
(b) Décrivez une technique permettant à l’ordonnanceur de s’assurer que les processus de haute priorité n’affament pas indéfiniment les processus de faible priorité.

(a) Technique d’ordonnancement pour prioriser les processus E/S

Une technique efficace est la réduction de la priorité d’un processus qui utilise entièrement sa tranche de temps. Lorsqu’un processus consomme sa tranche de temps sans se bloquer pour des E/S, cela indique qu’il est lié au CPU. En conséquence, l’ordonnanceur peut abaisser sa priorité. À l’inverse, les processus qui se bloquent pour des opérations E/S, souvent avec des durées d’utilisation du CPU plus courtes, conservent ou même augmentent leur priorité. Cela permet de favoriser les processus interactifs et ceux qui nécessitent des E/S, sans que l’utilisateur ait à spécifier explicitement leurs priorités.

(b) Technique pour éviter l’affamement des processus de faible priorité

Une technique pour garantir que les processus de haute priorité n’affament pas indéfiniment ceux de faible priorité est la réinitialisation périodique de la priorité des processus. Cette approche permet de restaurer les priorités des processus de faible priorité après un certain temps, leur donnant ainsi une chance de s’exécuter. Alternativement, l’utilisation d’une politique de round robin pour les processus de faible priorité peut également aider, en leur permettant d’obtenir des parts de temps régulières, même en présence de processus de haute priorité.

1.6) Considérons le programme C suivant. Supposons qu’il n’y ait pas d’erreurs de syntaxe et que le programme s’exécute correctement. Supposons que les appels système à fork réussissent. Quelle est la sortie affichée à l’écran lorsque nous exécutons le programme ci-dessous ?

void main(argc, argv) {
	for(int i = 0; i < 4; i++) {
		int ret = fork();
		if(ret == 0)
		printf("child %d\n", i);
	}
}

Analysons le programme C:

  • Boucle for : Elle itère de 0 à 3, soit 4 itérations.
  • À chaque itération, un appel à fork() est effectué, créant un nouveau processus.

Voici comment se déroulent les appels:

  • i = 0: 1 processus parent (P) et 1 processus enfant (C1) → sortie: "child 0" (C1).
  • i = 1: P et C1 forment 2 processus. P appelle fork() (C2) et C1 appelle fork() (C3) → sortie: "child 1" (C2 et C3).
  • i = 2: 4 processus (P, C1, C2, C3) effectuent fork() → sortie: "child 2" (C4, C5, C6, C7).
  • i = 3: 8 processus effectuent fork() → sortie: "child 3" (C8, C9, C10, C11, C12, C13, C14, C15).

À la fin, chaque processus enfant affichera "child i" une seule fois.

La sortie affichée à l'écran sera donc:

  • "child 0" (1 fois)
  • "child 1" (2 fois)
  • "child 2" (4 fois)
  • "child 3" (8 fois)

Résumé: Il y a 15 affichages au total pour les différentes valeurs de i.

1.7) Considérons un processus parent P qui a forké un processus enfant C dans le programme ci-dessous.

int a = 5;
int fd = open(...) //ouvrir un fichier
int ret = fork();
if(ret > 0) {
	close(fd);
	a = 6;
	...
}
else if(ret==0) {
	printf("a=%d\n", a);
	read(fd, str);
}

Une fois que le nouveau processus est forké, supposons que le processus parent est planifié en premier, avant le processus enfant. Lorsque le processus parent reprend après le fork, il ferme le descripteur de fichier et modifie la valeur d'une variable comme indiqué ci-dessus. Supposez que le processus enfant est ordonnancé pour la première fois seulement après que le parent a effectué ces deux changements.

(a) Quelle est la valeur de la variable 'a' telle qu'elle est affichée dans le processus enfant, lorsqu'il est programmé pour la prochaine fois? Expliquez pourquoi.
(b) La tentative de lecture du descripteur de fichier réussira-t-elle dans le processus enfant? Expliquez.

(a) Valeur de la variable a dans le processus enfant: La valeur de la variable a affichée dans le processus enfant sera 5. Cela s'explique par le fait que, lors du fork, l'enfant obtient une copie de l'espace mémoire du parent, y compris la valeur de a. Les modifications apportées par le parent (changement de a à 6) ne se répercutent pas dans l'espace mémoire du processus enfant, qui conserve la valeur initiale de a, soit 5.

(b) Réussite de la lecture du descripteur de fichier dans le processus enfant: La tentative de lecture du descripteur de fichier dans le processus enfant peut échouer. Lorsque le parent ferme le descripteur de fichier (close(fd)), cela ne ferme pas le descripteur dans l'enfant, car chaque processus a sa propre copie de la table des descripteurs de fichiers. Cependant, si le descripteur a été fermé dans le parent et que le fichier n'est pas ouvert en mode "lecture" ou que le descripteur a été mal configuré, la lecture peut échouer ou ne rien retourner. En général, tant que le fichier est correctement ouvert dans l'enfant avant que le parent ne ferme le descripteur, la lecture devrait fonctionner.

1.8) Considérons le pseudocode suivant. Supposons que tous les appels système aboutissent et qu'il n'y ait pas d'autres erreurs dans le code.

int ret1 = fork(); //fork1
int ret2 = fork(); //fork2
int ret3 = fork(); //fork3
wait();
wait();
wait();

Appelons P le processus parent d'origine de ce programme. Dessinez/décrivez un arbre généalogique de P et de tous ses descendants (enfants, petits-enfants, etc.) créés au cours de l'exécution de ce programme. Votre arbre doit avoir pour racine P. Faites apparaître les descendants engendrés comme des nœuds dans l'arbre, et reliez les processus liés par la relation parent-enfant par une flèche allant du parent à l'enfant. Donnez des noms contenant un numéro pour les descendants, où les processus enfants créés par la fork ci-dessus devraient avoir des numéros. Par exemple, les processus enfants créés par la fork 3 ci-dessus doivent porter les noms C31, C32, etc.

                 P
                / \
               /   \
              /     \
             /       \
            /         \
           /           \
        C11             C12
       /   \           /   \
    C21     C22     C23     C24
    / \     / \     / \     / \
  C31 C32 C33 C34 C35 C36 C37 C38

Description de l'arbre:

P est le processus parent d'origine.

fork1 (ret1):

  • Crée C11 (enfant de P).
  • Crée C12 (enfant de P).

fork2 (ret2):

  • C11 crée C21 (enfant de C11).
  • C11 crée C22 (enfant de C11).
  • C12 crée C23 (enfant de C12).
  • C12 crée C24 (enfant de C12).

fork3 (ret3):

  • Chaque processus crée 2 enfants:
  • C21 crée C31 et C32.
  • C22 crée C33 et C34.
  • C23 crée C35 et C36.
  • C24 crée C37 et C38.

1.9) Prenons l'exemple d'un processus parent qui a forké un processus enfant dans l'extrait de code ci-dessous.

int count = 0;
ret = fork();
if(ret == 0) {
	printf("count in child=%d\n", count);
}
else {
	count = 1;
}

Le parent exécute l'instruction « count = 1 » avant que l'enfant ne l'exécute pour la première fois. Quelle est la valeur de count affichée par le code ci-dessus ?

0 (l'enfant a sa propre copie de la variable)

Dans l'extrait de code donné:

  • Avant le fork: La variable count est initialisée à 0.
  • Lors de l'appel à fork(), le processus parent est dupliqué, créant un processus enfant.
  • Dans le processus enfant (où ret == 0), printf("count in child=%d\n", count); est exécuté avant que le parent ne modifie count.
  • Dans le processus parent, count est modifié à 1.

Étant donné que le processus enfant a sa propre copie de l'espace mémoire (y compris la variable count), la valeur de count dans le processus enfant reste 0.

 

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *