Système d'Exploitation II
Question 1 - Qui suis-je ? Question 1(a) - Zone de données non initialisées Je suis le Segment BSS . (BSS signifie Block Started by Symbol , qui est la zone mémoire allouée par le système d'exploitation pour les variables globales ou statiques non initialisées). Question 1(b) - Commande d'affichage des tailles de segments Je suis la commande size .
D'après le document Système d'Exploitation II
Cet article a été rédigé automatiquement à partir du document source, puis vérifié avant publication.

Document source
Programming · PDF · 7 pages · 2017
Afficher l'aperçu du document
Question 1 - Qui suis-je ?
Question 1(a) - Zone de données non initialisées
Je suis le Segment BSS. (BSS signifie Block Started by Symbol, qui est la zone mémoire allouée par le système d'exploitation pour les variables globales ou statiques non initialisées).
Question 1(b) - Commande d'affichage des tailles de segments
Je suis la commande size. Sous les systèmes de type UNIX, cette commande permet d'afficher la taille du segment de texte (code), du segment de données et du segment BSS d'un fichier exécutable.
Question 1(c) - Stratégie d'optimisation de mémoire
Je suis la stratégie Copy on write (Copie sur modification). Elle permet à plusieurs processus de pointer vers la même ressource en mémoire physique tant qu'ils ne font que la lire. Dès qu'un processus tente de la modifier, le système crée une copie distincte de cette ressource pour ce processus.
Question 1(d) - Création d'un processus
Je suis l'appel système fork. Il demande au système d'exploitation de créer un nouveau processus (le processus enfant) en dupliquant le processus appelant (le processus parent).
Question 2 - Définitions (Concurrence et synchronisation)
Question 2(a) - Objet critique
Un objet critique est un objet qui ne peut être accédé simultanément par plusieurs processus sans risquer une incohérence des données.
Question 2(b) - Section critique
Une section critique est un ensemble de suites d'instructions qui opèrent sur un ou plusieurs objets critiques. Son accès doit être protégé pour garantir qu'un seul processus à la fois puisse exécuter ces instructions.
Question 3 - Propriétés d'accès exclusif à une section critique
Question 3(a) - Propriété 1 : Unicité
Unicité (ou exclusion mutuelle) : Un et un seul processus peut se trouver en section critique à un instant donné.
Question 3(b) - Propriété 2 : Condition de progression
Pas d'excès de politesse (Condition de progression) : Si plusieurs processus sont en attente pour entrer dans leur section critique, alors qu'aucun processus ne se trouve actuellement en section critique, l'un d'eux doit pouvoir y rentrer au bout d'un temps fini.
Question 3(c) - Propriété 3 : Non interblocage
Non interblocage (Absence d'interblocage) : Si un processus est bloqué hors de sa section critique (par exemple, il a terminé ou il a planté ailleurs), ce blocage ne doit pas empêcher les autres processus d'entrer dans la section critique.
Question 3(d) - Propriété 4 : Équité
Équité (Attente bornée) : Il n'existe aucun privilège entre les divers processus. L'entrée dans la section critique ne doit pas dépendre de la volonté d'un processus particulier, garantissant ainsi qu'aucun processus ne subisse de famine.
Question 4 - Gestion des Processus (Arborescence et exécution)
Question 4(a) - Nombre et arborescence des processus créés
Le programme crée un total de 5 processus (incluant le processus parent initial).
Explication détaillée de l'exécution :
- Le processus initial P1 exécute
p1 = fork();et crée l'enfant P2. - Ensuite, P1 et P2 exécutent tous les deux la ligne
p2 = fork();.- P1 crée P3.
- P2 crée P4.
if (p1 * p2 > 0) fork();. Seul P1 possède à la fois un p1 strictement positif (le PID de P2) et un p2 strictement positif (le PID de P3). Les enfants reçoivent toujours la valeur 0 pour le fork() qui les crée. Par conséquent, seul P1 entre dans le bloc if et crée P5.Arborescence finale :
- P1
- P2
- P4
- P3
- P5
- P2
Question 4(b) - Valeurs de p1 et p2 affichées par chaque processus
Lors du dernier printf, les variables ont hérité des valeurs retournées par les appels fork() respectifs. Voici ce que chaque processus affichera :
- Processus P1 : Il a créé P2 (donc p1 = PID-P2) et P3 (donc p2 = PID-P3).
Affichage :
p1 = PID-P2, p2 = PID-P3 - Processus P2 : Il est l'enfant du premier fork (donc p1 = 0). Il a ensuite créé P4 (donc p2 = PID-P4).
Affichage :
p1 = 0, p2 = PID-P4 - Processus P3 : Il hérite de la variable
p1de P1 juste avant sa création (donc p1 = PID-P2). Il est l'enfant du deuxième fork de P1 (donc p2 = 0). Affichage :p1 = PID-P2, p2 = 0 - Processus P4 : Il hérite de la variable
p1de P2 (donc p1 = 0). Il est l'enfant du deuxième fork de P2 (donc p2 = 0). Affichage :p1 = 0, p2 = 0 - Processus P5 : Il hérite de l'état complet de P1 juste après la création de P3. Il possède donc les mêmes valeurs locales que P1 au moment de sa propre création par le troisième
fork(). Affichage :p1 = PID-P2, p2 = PID-P3
Question 4(c) - Prévention des processus orphelins (Correction de code)
Pour éviter que le processus parent ne se termine avant ses enfants (laissant ainsi ces derniers orphelins et adoptés par le processus init), il faut ordonner au parent d'attendre la fin de l'exécution des processus fils en appelant wait().
Note : Pour que le code soit strictement valide et compilable en C moderne, les bibliothèques requises par les appels systèmes UNIX (<unistd.h>, <sys/types.h>, <sys/wait.h>) ont été ajoutées.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void) {
pid_t p1, p2;
p1 = fork();
p2 = fork();
if (p1 * p2 > 0) {
fork();
}
printf("Processus %d : p1=%d, p2=%d\n", getpid(), p1, p2);
/* Le processus parent attend un processus fils pour éviter qu'il ne devienne orphelin */
wait(NULL);
return 0;
}
Question 5 - Gestion des Threads (Correction de code)
Le code d'origine comportait 4 erreurs empêchant sa compilation et sa bonne exécution. Voici le code réparé (et rendu valide avec le retour standard de la fonction du thread) :
/* Correction 1 : Inclusion obligatoire pour manipuler les threads POSIX */
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
/* Correction 2 : Déclaration du prototype de la fonction associée au thread avant main() */
void *fonction_thread(void *arg);
int main(void) {
pthread_t thr;
int *ptr_int;
ptr_int = malloc(sizeof(int));
if (ptr_int == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
*ptr_int = 19;
/* Correction 3 : pthread_create prend l'adresse de thr (&thr) et non la valeur */
if (pthread_create(&thr, NULL, fonction_thread, ptr_int) != 0) {
fprintf(stderr, "Erreur dans pthread_create\n");
exit(EXIT_FAILURE);
}
pthread_join(thr, NULL);
fprintf(stderr, "Thread Main\n");
return 0;
}
/* Correction 4 : Signature de la fonction. Elle doit accepter un (void *) et retourner un (void *) */
void *fonction_thread(void *arg) {
int *ptr_int_2 = (int *)arg;
int temp = *ptr_int_2;
free(arg);
return NULL; /* Ajouté pour garantir que la fonction retourne bien un type void * (standard C) */
}
Les 4 erreurs corrigées étaient :
- L'omission de la bibliothèque
#include <pthread.h>, indispensable pour l'utilisation des types (commepthread_t) et des primitives des threads POSIX. - L'absence de déclaration préalable du prototype de
fonction_thread, dontmain()a pourtant besoin lors de son appel àpthread_create. - Le passage d'arguments incorrect dans
pthread_create. Le premier argument exige un pointeur vers unpthread_t. Il fallait écrire&thr(l'adresse) plutôt quethr. - La signature invalide de la routine du thread. L'API pthreads impose que la fonction pointée accepte un pointeur générique
void *arget retourne unvoid *. La signature a été corrigée devoid fonction_thread()versvoid *fonction_thread(void *arg).
Question 6 - Propriétés exclusives d'un thread
Un thread dispose exclusivement des ressources nécessaires pour maintenir son propre contexte d'exécution. Les bonnes réponses sont :
- A. un identifiant (Thread ID)
- C. un program counter (Compteur ordinal, pointant sur l'instruction courante du thread)
- D. un ensemble de registres (État du processeur propre au thread)
- E. une pile (Stack, pour les variables locales et les appels de fonctions propres au thread)
(Note : Une section de données ou de code est toujours partagée.)
Question 7 - Propriétés partagées d'un thread
Un thread partage avec les autres threads du même processus les ressources globales et le code du programme. Les bonnes réponses sont :
- B. une section de données (Variables globales, tas, BSS)
- F. une section de code (Les instructions exécutables du programme)
Méthode
Face à ce type d'examen sur les systèmes d'exploitation, appliquez les méthodes suivantes :
- Tracage des appels
fork(): Il s'agit d'un exercice incontournable. Ne le faites jamais de tête. Prenez un brouillon et dessinez un graphe à chaque appel. Marquez explicitement les variables locales pour chaque processus au moment de sa création. Souvenez-vous que le parent reçoit le PID (strictement positif) de l'enfant, tandis que l'enfant reçoit 0. - Codes en C (Threads et Processus) : Cherchez systématiquement les erreurs de pointeurs (oublis du caractère
&ou*), les signatures de fonctions pthreads (qui manipulent toujours duvoid *), et l'absence des bibliothèques (<pthread.h>,<unistd.h>). Rappelez-vous également la nécessité d'orchestrer la fin des processus (wait()) ou des threads (pthread_join()). - Théorie et définitions : Les concepts de synchronisation (exclusion mutuelle, interblocage, équité) nécessitent de connaître les termes formels par cœur. Rédigez vos définitions de façon précise pour montrer au correcteur que vous ne confondez pas le problème (la ressource partagée critique) et la solution (la zone de code protégée).
Commentaires
Aucun commentaire pour le moment. Posez la première question.