Spaceuit Logo Spaceuit Plateforme Éducative

Informatique Appliquée

Fillière Intelligence Artificielle

Hiver 2025

Ali El Hourch
[email protected]


Guide
Nouveau
Assistant AI
Visual Studio Code Google Classroom Whatsapp Whatsapp Discord Discord
Module 1: Recherche Opérationelle et optimisation Module 2: Base de données Module 3: Réseaux Module 4: Système d'exploitation 2 Module 5: Programmation objet en C++ Module 6: Langues Etrangéres Module 7: Développement Personnel

Semestres

Hiver 2025

Tronc Communs

Informatique Appliquée

Série 1 Système Exploitation


Série 1


Outils


  • Pour ce module, il est recommandé d’utiliser Visual Studio Code, qui permet d’exécuter simultanément plusieurs terminaux, y compris Bash. Ce dernier offre la possibilité d’exécuter des commandes similaires à celles que l’on trouve dans les systèmes UNIX, comme par exemple PS. Pour suivre ce module, vous aurez besoin de :
    1. VSCode
    2. Les bibliothèques C (MINGW)
    3. Un shell Bash pour le terminal (ou Git Bash)

VSCode et Git Bash


  • La vidéo ci-dessous vous montre comment télécharger et installer VSCode et Git Bash. Pour installer VSCode, rendez-vous sur le site officiel de Visual Studio Code, sélectionnez la version correspondant à votre système d'exploitation, téléchargez le programme d'installation et suivez les instructions à l'écran.
  • Pour Git Bash, visitez le site officiel de Git, choisissez la version adaptée à votre OS, puis téléchargez et installez le logiciel en suivant les étapes proposées.

Les bibliothèques C (MINGW)


  • La vidéo ci-dessous vous montre comment télécharger et installer les bibliothèques C (MINGW). Pour installer MINGW, rendez-vous sur le site officiel de MinGW ou optez pour MinGW-w64 si vous avez besoin d'une version 64 bits. Téléchargez le programme d'installation correspondant à votre système d'exploitation, lancez-le et suivez les instructions à l'écran pour configurer votre environnement de développement en C.

Terminal Bash


  • Après avoir installé VSCode, Git (avec Git Bash) et MINGW, ouvrez VSCode et accédez au menu Terminal > Nouveau Terminal. Dans le panneau du terminal, cliquez sur l’icône de sélection du profil (généralement en haut à droite) et choisissez Git Bash. Une fois sélectionné, le terminal s’ouvrira en mode Bash, vous permettant d’exécuter vos commandes comme dans un environnement UNIX.
  • Dans mon cas, j'ai un système UNIX et donc j'ai déjà bash intégré dans mon système (et non pas git bash) mais il s'agit de la même chose.

Exercice 1


  1. Exécutez la commande ps et interprétez le résultat obtenu.
    Indication
    • Utilisez ps pour afficher la liste des processus actifs.
    • Observez les colonnes comme PID, TTY, TIME et CMD.
    Correction
    • $ ps
      ┌────────┬─────────┬─────────┬────────────┐
      │  PID   │   TTY   │   TIME  │     CMD    │
      ├────────┼─────────┼─────────┼────────────┤
      │  1234  │ pts/0   │ 00:00:00│ /bin/bash  │
      │  5678  │ pts/0   │ 00:00:00│ ps         │
      └────────┴─────────┴─────────┴────────────┘
      
      $ ps -f
      ┌──────┬────────┬────────┬─────┬────────┬─────────┬─────────┬─────────────────────────────┐
      │ UID  │  PID   │  PPID  │  C  │ STIME  │   TTY   │   TIME  │           CMD               │
      ├──────┼────────┼────────┼─────┼────────┼─────────┼─────────┼─────────────────────────────┤
      │ user │  1234  │    1   │  0  │ 09:50  │ pts/0   │ 00:00:01│ /bin/bash                   │
      │ user │  5678  │  1234  │  0  │ 09:51  │ pts/0   │ 00:00:00│ ps -f                       │
      └──────┴────────┴────────┴─────┴────────┴─────────┴─────────┴─────────────────────────────┘
      
    • Le résultat affiche des informations sur les processus en cours d'exécution. L'affichage minimal (ps) présente les attributs essentiels :
      - PID : l'identifiant du processus.
      - TTY : le terminal associé.
      - TIME : le temps CPU consommé.
      - CMD : la commande exécutée.

      L'affichage détaillé (ps -f) ajoute des informations supplémentaires :
      - UID : l'utilisateur ayant lancé le processus.
      - PID : l'identifiant du processus.
      - PPID : l'identifiant du processus parent.
      - C : l'utilisation du CPU en pourcentage.
      - STIME : l'heure de démarrage du processus.
      - TIME : le temps CPU cumulé.
      - CMD : la commande complète avec ses arguments.
      Ces informations permettent de surveiller, diagnostiquer et gérer les processus du système.
  2. Créer un script qui affiche bonjour toutes les 15 secondes, le lancer en arrière-plan et afficher son PID ainsi que son numéro de job.
    Indication
    • Créez un script (bonjour.sh) qui contient une boucle infinie avec sleep 15 pour la temporisation.
    • Utilisez la commande echo "bonjour" dans la boucle.
    • La syntaxe d'une boucle infinie est la suivante :
      while true 
      do 
      # instructions ... 
      done
      
    • Pour exécuter un script Bash (.sh), commencez par le rendre exécutable avec :
    • chmod +x chemin/script.sh
      
    • Ensuite, lancez-le en précisant son chemin, par exemple :
    • ./script.sh
      
    • Pour exécuter un script en arrière-plan, ajoutez le symbole & à la fin de la commande (exemple : ./script.sh &). Cela permet de libérer le terminal pour d'autres tâches et de continuer l'exécution du script sans bloquer l'interface utilisateur.
    • Affichez le PID en utilisant ps et le numéro de job avec jobs -l ou jobs -p.
    Correction
      #!/bin/bash
      while true ; do
      echo "hello"
      sleep 15
      done
      
      $ ./bonjour.sh &
      [1] 94671
      hello
      hello
      ps
      ┌──────┬────────┬────────┬─────┬────────┬─────────┬─────────┬─────────────────────────────────────┐
      │ UID  │  PID   │  PPID  │  C  │ STIME  │   TTY   │   TIME  │               CMD                   │
      ├──────┼────────┼────────┼─────┼────────┼─────────┼─────────┼─────────────────────────────────────┤
      │ user │ 92554  │    1   │  0  │ 09:50  │ pts/2   │ 00:00:21│ /bin/bash --init-file /tmp/AppTransl│
      │ user │ 94671  │ 92554  │  0  │ 09:51  │ pts/2   │ 00:00:01│ /bin/bash ./bonjour.sh              │
      │ user │ 94736  │ 94671  │  0  │ 09:51  │ pts/2   │ 00:00:00│ sleep 15                            │
      └──────┴────────┴────────┴─────┴────────┴─────────┴─────────┴─────────────────────────────────────┘
      jobs -l
      [1]+ 94671 Running                 ./bonjour.sh &
      jobs -p
      94671
      
    • jobs désigne les processus en arrière-plan ou suspendus associés à votre session de shell (Terminal).
    • La commande jobs -l affiche des informations détaillées incluant les ID de processus (PIDs) et les numéros de job, tandis que jobs -p ne liste que les ID de processus.
  3. Remettez le script au premier plan avec la commande fg après l'avoir suspendu avec CTRL+Z et affichez son état avec jobs -l.
    Indication
    • Remettez le script au premier plan avec la commande fg.
    • Utilisez CTRL+Z pour suspendre temporairement le script.
    • Exécutez jobs -l pour voir son état.
    Correction
      $ fg
      ./bonjour.sh
      hello
      ^Z
      [1]+  Stopped                 ./bonjour.sh
      $ jobs -l
      [1]+  1514 Suspended: 18           ./bonjour.sh
      
    • En exécutant fg dès le début, vous faites passer le script en premier plan, ce qui fait que le script s'exécute normalement et affiche hello.
    • Après l'exécution, en appuyant sur CTRL+Z, vous suspendrez le script, ce qui est indiqué par le message [1]+ Stopped ./bonjour.sh.
    • L'exécution de jobs -l montre ensuite les détails du job suspendu, notamment son PID (1514) et son état (Suspended: 18), confirmant que le script est bien suspendu.
  4. Basculer le script en tâche de fond en utilisant bg et observer l’affichage de bonjour.
    Correction
      $ bg
      [1]+ ./bonjour.sh &
      hello
      $ hello
      ...
      
  5. Tuer ce processus en utilisant son PID ou son numéro de job.
    Indication
    • Utilisez kill suivi du PID ou kill %job_number.
    Correction
      $ ps
      ┌───────┬────────┬─────────┬───────────────────────┐
      │  PID  │  TTY   │  TIME   │          CMD          │
      ├───────┼────────┼─────────┼───────────────────────┤
      │  1120 │ ttys236│ 0:00.08 │ /bin/bash             │
      │  1514 │ ttys236│ 0:00.04 │ /bin/bash ./bonjour.sh│
      │  2218 │ ttys236│ 0:00.00 │ sleep 15              │
      └───────┴────────┴─────────┴───────────────────────┘
      
      $ kill 1514
      [1]+  Terminated: 15          ./bonjour.sh
      
  6. Lancer à nouveau ce script en mode détaché du terminal à l’aide de nohup.
    Indication
    • Utilisez nohup ./bonjour.sh & pour lancer le script.
    • Le script tourne désormais en arrière-plan et reste actif même après déconnexion.
    Correction
      $ nohup ./bonjour.sh &
      
    • La commande $ nohup ./bonjour.sh & exécute le script bonjour.sh en arrière-plan tout en empêchant son arrêt lors de la déconnexion de l'utilisateur.
    • Cela signifie que même si vous fermez votre terminal, le code continuera à s'exécuter en arrière-plan.
    • Les sorties du code sont redirigées vers un autre fichier appelé nohup.out
  7. Après déconnexion et reconnexion, afficher les processus avec ps et expliquer pourquoi le processus bonjour n’apparaît pas.
    Indication
    • La commande ps par défaut liste uniquement les processus de la session courante.
    Correction
    • Le processus lancé avec nohup est détaché du terminal et n’apparaît pas dans la nouvelle session.
    • Ici, nous voyons que même après avoir fermé le terminal, les sorties du fichier ./bonjour.sh sont toujours ajoutées au fichier nohup.out. Par conséquent, ps ne traite que ce qui est lié au terminal actuel.
  8. Tuer le processus bonjour et supprimer son fichier de sortie.
    Indication
    • Utilisez kill pour terminer le processus identifié par son PID ou numéro de job.
    • Pour trouver le PID du processus ./bonjour.sh, vous pouvez utiliser :
      $ ps aux | grep bonjour.sh
      
    Correction
      $ ps aux | grep bonjour.sh
      alielhourch       3883   0.0  0.0 410199840   1312 s242  S+    3:08PM   0:00.00 grep bonjour.sh
      alielhourch       3264   0.0  0.0 410209584   2016   ??  S     2:51PM   0:00.17 /bin/bash ./bonjour.sh
      
      $ kill 3264
      
      $ ps aux | grep bonjour.sh
      alielhourch       3900   0.0  0.0 410199840   1312 s242  S+    3:08PM   0:00.00 grep bonjour.sh
      $ rm nohup.out
      
    • La première commande affiche deux processus : l'un correspondant à l'exécution de "bonjour.sh" (PID 3264) et l'autre au processus "grep bonjour.sh" lui-même (PID 3883).
    • La commande "kill 3264" termine le processus "bonjour.sh".
    • La commande suivante ne montre que le processus "grep bonjour.sh", indiquant que "bonjour.sh" a été correctement arrêté.
    • La commande rm nohup.out permet de supprimer le fichier de sortie.

Exercice 2


  • Donner le nombre des messages « Hello » affichées.
  • int main() {
        fork();
        printf("hello!\n");
        exit(0);
    }
    
    int main() {
        fork();
        fork();
        printf("hello!\n");
        exit(0);
    }
    
    Correction
    • Dans le premier programme, un seul appel à fork() crée un processus enfant. Au total, il y a donc 2 processus qui exécutent le printf(), affichant ainsi « hello » 2 fois.
    • Dans le deuxième programme, deux appels successifs à fork() créent 2 x 2 = 4 processus, chacun exécutant le printf(). Le message « hello » est donc affiché 4 fois.

Exercice 3


  • Écrire un programme créant un processus fils. Le processus père affichera son identifiant ainsi que celui de son fils, le processus fils affichera son identifiant ainsi que celui de son père. Quel est le PID du père affiché par le fils ?
  • Indication
    • Utilisez la bibliothèque suivante :
      • unistd.h
        Cette bibliothèque inclut diverses fonctions de l’API POSIX, notamment fork(), getpid() et getppid(). fork() est utilisé pour créer un nouveau processus (le fils), tandis que getpid() et getppid() servent à obtenir respectivement l’identifiant du processus courant et celui du processus parent.
    • La fonction fork() permet de créer un nouveau processus en dupliquant le processus appelant. Son retour permet de différencier le processus père du processus fils :
      0 : Le retour est égal à 0 dans le processus fils.
      PID positif : Le processus père reçoit l’identifiant (PID) du processus fils.
      -1 : En cas d’échec, fork() retourne -1, indiquant que la création du processus a échoué.
    Correction
      #include <stdio.h>
      #include <unistd.h>
      #include <stdlib.h>
      
      int main() {
          int f = fork();
      
          switch(f) {
              case -1:
                  printf("\nErreur: Le processus n'est pas créé.\n");
                  exit(EXIT_FAILURE);
              
              case 0:
                  printf("\nJe suis le processus fils.\nMon PID est: %d\nMon père est: %d\n", getpid(), getppid());
                  break;
              
              default:
                  printf("\nJe suis le père.\nMon PID est: %d\nMon fils est: %d\n", getpid(), f);
                  break;
          }
      
          exit(EXIT_SUCCESS);
      }
      
      Je suis le père.
      Mon PID est: 7796
      Mon fils est: 7797
      
      Je suis le processus fils.
      Mon PID est: 7797
      Mon père est: 1
      
    • Lorsque le processus père se termine avant le fils, ce dernier devient un processus « orphelin ». Sur les systèmes UNIX/Linux, tous les processus orphelins sont automatiquement réassignés au processus init (PID = 1). C’est pour cela que le fils affiche « 1 » comme identifiant de son père.

Exercice 4


  • Afin de ralentir l’exécution du père pour que le fils termine son exécution avant le père, ajouter un appel de la fonction sleep() de façon à afficher le PID du père par le fils.
  • Indication
    • Utilisez la bibliothèque suivante :
      • stdlib.h
        Cette bibliothèque incluant la fonction sleep() qui suspend l’exécution du processus appelant pendant un nombre de secondes défini. Cela signifie que le programme “met en pause” ses opérations pendant la durée spécifiée avant de reprendre l’exécution. Par exemple, si vous appelez sleep(5), le processus attendra 5 secondes. Ce délai peut être utile pour créer des temporisations, gérer la synchronisation entre processus ou simplement ralentir l’exécution d’une boucle.
    Correction
      #include <stdio.h>
      #include <unistd.h>
      #include <stdlib.h>
      
      int main() {
          int f = fork();
      
          switch(f) {
              case -1:
                  printf("\nErreur: Le processus n'est pas crée.\n");
                  exit(EXIT_FAILURE);
              
              case 0:
                  printf("\nJe suis le processus fils.\nMon PID est: %d\nMon père est: %d\n", getpid(), getppid());
                  break;
              
              default:
                  sleep(2);
                  printf("\nJe suis le père.\nMon PID est: %d\nMon fils est: %d\n", getpid(), f);
                  break;
          }
      
          exit(EXIT_SUCCESS);
      }
      

Exercice 5


  • Ajouter un appel de la fonction wait(NULL) pour éviter que le père ne se termine pas avant le fils.
  • Indication
    • Utilisez la bibliothèque suivante :
      • sys/wait.h
        Ce bibliothèque contient les déclarations des fonctions de synchronisation des processus, comme wait() ou waitpid(). Ces fonctions sont utilisées pour que le processus père attende la fin du processus fils, évitant ainsi la création de processus zombies, même si dans cet exemple, elles ne sont pas explicitement appelées.
    • La fonction wait() est utilisée pour faire attendre le processus parent jusqu’à ce qu’un de ses processus enfants se termine. Elle est souvent appelée de cette manière :
      int status;
      int pid = wait(&status);
      
      Ou :
      int pid = wait(NULL);
      
    • La fonction wait() bloque l’exécution du processus parent jusqu’à ce qu’un de ses processus enfants se termine. Lorsque l’enfant se termine, wait() renvoie le PID de ce processus terminé. De plus, si vous passez l’adresse d’une variable (comme dans wait(&status)), le statut de terminaison du processus enfant est stocké dans cette variable. Ce statut contient des informations sur la façon dont l’enfant s’est terminé (par exemple, s’il a terminé normalement via exit() ou s’il a été interrompu par un signal). Si aucun enfant n’est disponible ou en cas d’erreur lors de l’appel à wait(), la fonction retourne -1.
    Correction
      #include <stdio.h>
      #include <unistd.h>
      #include <sys/wait.h>
      #include <stdlib.h>
      
      int main() {
          int f = fork();
      
          switch(f) {
              case -1:
                  printf("\nErreur: Le processus n'est pas crée.\n");
                  exit(EXIT_FAILURE);
              
              case 0:
                  printf("\nJe suis le processus fils.\nMon PID est: %d\nMon père est: %d\n", getpid(), getppid());
                  break;
              
              default:
                  wait(NULL);
                  printf("\nJe suis le père.\nMon PID est: %d\nMon fils est: %d\n", getpid(), f);
                  break;
          }
      
          exit(EXIT_SUCCESS);
      }
      
      Je suis le processus fils.
      Mon PID est: 7740
      Mon père est: 7739
      
      Je suis le père.
      Mon PID est: 7739
      Mon fils est: 7740
      

Exercice 6


  • Dans l’exercice précédent, utiliser wait() pour obtenir le code de retour du fils et l’afficher.
  • Indication
    • Utilisez la bibliothèque suivante :
      • sys/wait.h
        Ce bibliothèque contient les déclarations des fonctions de synchronisation des processus, comme wait() ou waitpid(). Ces fonctions sont utilisées pour que le processus père attende la fin du processus fils, évitant ainsi la création de processus zombies, même si dans cet exemple, elles ne sont pas explicitement appelées.
    • La fonction wait() est utilisée pour faire attendre le processus parent jusqu’à ce qu’un de ses processus enfants se termine. Elle est souvent appelée de cette manière :
      int status;
      int pid = wait(&status);
      
      Ou :
      int pid = wait(NULL);
      
    • La fonction wait() bloque l’exécution du processus parent jusqu’à ce qu’un de ses processus enfants se termine. Lorsque l’enfant se termine, wait() renvoie le PID de ce processus terminé. De plus, si vous passez l’adresse d’une variable (comme dans wait(&status)), le statut de terminaison du processus enfant est stocké dans cette variable. Ce statut contient des informations sur la façon dont l’enfant s’est terminé (par exemple, s’il a terminé normalement via exit() ou s’il a été interrompu par un signal). Si aucun enfant n’est disponible ou en cas d’erreur lors de l’appel à wait(), la fonction retourne -1.
    • La macro WEXITCODE(status), définie dans la bibliothèque "sys /wait.h", permet d’extraire le code de sortie (le retour passé à exit() ou retourné par main) d’un processus terminé. Par exemple, si le processus enfant se termine en appelant exit(42), le parent récupère un entier status via wait(&status). En appliquant WEXITCODE(status), on obtient alors la valeur 42.
    • Explication détaillée

    • La valeur retournée par wait() est souvent un entier de 16 bits où plusieurs informations sont encodées. Le code de sortie proprement dit se trouve dans les 8 bits de poids fort. Par exemple, si un processus enfant se termine en appelant exit(42), le code 42 est stocké dans les bits supérieurs.
    • Prenons un exemple :
      1. Le code 42 en binaire sur 8 bits s’écrit :
        00101010 00000000
      2. Ce code est placé dans les 8 bits de poids fort du statut retourné par wait(). Ainsi, le statut complet (16 bits) devient :
        00101010 00000000
      3. Pour extraire le code de sortie, on effectue un décalage à droite de 8 bits (équivalent à diviser par 256). En faisant : (status >> 8), on obtient à nouveau 00101010, soit 42 en décimal.
    • La macro WEXITCODE(status) réalise exactement cette opération de décalage pour fournir le code de sortie du processus enfant.
    • WEXITCODE(status) = status >> 8 
      
    Correction
      #include <stdio.h>
      #include <unistd.h>
      #include <sys/wait.h>
      #include <stdlib.h>
      
      int main() {
          int f = fork();
      
          switch(f) {
              case -1:
                  printf("\nErreur: Le processus n'est pas crée.\n");
                  exit(EXIT_FAILURE);
              
              case 0:
                  printf("\nJe suis le processus fils.\nMon PID est: %d\nMon père est: %d\n", getpid(), getppid());
                  exit(10);
                  break;
      
              default: {
                  int status;
                  wait(&status);
                  int codeStatus = WEXITSTATUS(status); // Equivalent à status >> 8
                  printf("\nJe suis le père.\nMon PID est: %d\nMon fils est: %d\nLe code de status du mon fils est: %d\n"
                              ,getpid(), f, codeStatus);
                  break;
              }
          }
      }
      
      Je suis le processus fils.
      Mon PID est: 11569
      Mon père est: 11566
      
      Je suis le père.
      Mon PID est: 11566
      Mon fils est: 11569
      Le code de status du mon fils est: 10 
      

Exercice 7


  • Ecrire un programme qui crée 10 processus fils. Chacun d’entre eux devra afficher dix fois d’affilée son numéro d’ordre entre 0 et 9.
  • Correction
      #include <stdio.h>
      #include <unistd.h>
      #include <sys/wait.h>
      #include <stdlib.h>
      
      int main() {
          for(int i = 0; i < 10; i++){
              int f = fork();
      
              switch(f){
                  case -1:
                      printf("Erreur: Le processus n'est pas crée.\n");
                      exit(EXIT_FAILURE);
                  
                  case 0:
                      printf("\nJe suis le fils num %d.\nMon code 10 fois est: ", i);
                      
                      for(int j = 0; j < 10; j++) 
                          printf("%d ", i);
                      
                      printf("\n");
                      exit(EXIT_SUCCESS);
                  
                  default:
                      wait(NULL);
              }
          }
      
          exit(EXIT_SUCCESS);
      }
      
      Je suis le fils num 0.
      Mon code 10 fois est: 0 0 0 0 0 0 0 0 0 0 
      
      Je suis le fils num 1.
      Mon code 10 fois est: 1 1 1 1 1 1 1 1 1 1 
      
      Je suis le fils num 2.
      Mon code 10 fois est: 2 2 2 2 2 2 2 2 2 2 
      
      Je suis le fils num 3.
      Mon code 10 fois est: 3 3 3 3 3 3 3 3 3 3 
      
      Je suis le fils num 4.
      Mon code 10 fois est: 4 4 4 4 4 4 4 4 4 4 
      
      Je suis le fils num 5.
      Mon code 10 fois est: 5 5 5 5 5 5 5 5 5 5 
      
      Je suis le fils num 6.
      Mon code 10 fois est: 6 6 6 6 6 6 6 6 6 6 
      
      Je suis le fils num 7.
      Mon code 10 fois est: 7 7 7 7 7 7 7 7 7 7 
      
      Je suis le fils num 8.
      Mon code 10 fois est: 8 8 8 8 8 8 8 8 8 8 
      
      Je suis le fils num 9.
      Mon code 10 fois est: 9 9 9 9 9 9 9 9 9 9