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.

Système d'Exploitation II

Document source

Système d'Exploitation II

Programming · PDF · 7 pages · 2017

Afficher l'aperçu du document

Consulter le document original →

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 :

  1. Le processus initial P1 exécute p1 = fork(); et crée l'enfant P2.
  2. Ensuite, P1 et P2 exécutent tous les deux la ligne p2 = fork();.
    • P1 crée P3.
    • P2 crée P4.
  • Vient ensuite la condition 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

    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 p1 de 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 p1 de 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 :

    1. L'omission de la bibliothèque #include <pthread.h>, indispensable pour l'utilisation des types (comme pthread_t) et des primitives des threads POSIX.
    2. L'absence de déclaration préalable du prototype de fonction_thread, dont main() a pourtant besoin lors de son appel à pthread_create.
    3. Le passage d'arguments incorrect dans pthread_create. Le premier argument exige un pointeur vers un pthread_t. Il fallait écrire &thr (l'adresse) plutôt que thr.
    4. La signature invalide de la routine du thread. L'API pthreads impose que la fonction pointée accepte un pointeur générique void *arg et retourne un void *. La signature a été corrigée de void fonction_thread() vers void *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 :

    1. 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.
    2. 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 du void *), 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()).
    3. 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).

    Partager

    Commentaires

    Aucun commentaire pour le moment. Posez la première question.

    Les commentaires sont relus avant publication. Votre e-mail n'est jamais affiché.

    ← Toutes les révisions