Les processus

Un processus est un programme en cours d'exécution.

Un programme est donc un élément passif (un ensemble d'octets sur le disque) tandis qu'un processus est un élément actif (un programme en cours d'exécution).

Que comporte un processus ?

Informations concernant le processus

Etat

2023-09-26_11-56-12_screenshot.png

Vous pouvez avoir plus d’information sur ce sujet en consultant ce site.

Pour exécuter plusieurs processus

Le système alterne très vite entre les différents états pour donner l'illusion que plusieurs processus s'exécutent en même temps.

En somme on garde en mémoire les processus, le scheduler va choisir les processus à exécuter; lorsqu'un processus est en attente un autre processus va être sélectionné pour être exécuté. Le but du scheduler est de maximiser l'utilisation du CPU.

Le scheduler

Le scheduler va sélectionner le processus à exécuter, c'est lui qui va alterner entre les différents états de chaques processus.

Le scheduler utilise un algorithme précis et il doit être le plus rapide possible.

Le scheduler classifie les processus selon leur type :

On va toujours vouloir priviléger les processus entrée-sorties, qui sont ceux qui dialogues avec l'utilisateur et qui vont donner l'illusion que les choses d'exécutent en même temps.

Changement de contexte

Pour changer de processus on doit pouvoir sauvegarder le contexte (les données) du processus précédent.

Le système va donc sauvegarder toutes les informations du processus pour pouvoir le redémarrer plus tard.

Ensuite le scheduler va sélectionner un autre processus et en charger les informations/contexte pour le démarrer.

Il va ainsi faire cela tout le temps pour alterner entre tous les processus en attente, prêts et en cours pour maximiser l'utilisation du CPU et donner l'illusion que tout fonctionne en même temps.

Création d'un processus (fork)

2023-09-26_12-14-38_screenshot.png

Pour créer un processus on utilise l'appel système fork. Le processus créé par un fork est appelé le processus fils, et le processus qui a créé le fils est appelé le père.

Le processus fils est un clone de son père, toutes les données du premier sont recopiées dans le fils.

La fonction fork() en C va retourner un entier :

Exemples en C

Exemple simple

Voici un autre exemple :

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
int main (void)
{
    /* Variable pour stoquer la réponse du fork */
    pid_t pid;

    /* Fork et mise du résultat dans la variable */
    pid = fork();

    /* Si le pid est 0, alors c'est le fils qui lit l'info */
    if (pid == 0) {
      printf("Je suis le processus fils\n");

    /* Si le pid est autre chose, alors c'est le père qui lit l'info */
    } else {
      printf("Je suis le processus père et mon fils est le : %d\n", pid);
    }

    /* Fin des deux processus */
    return EXIT_SUCCESS;
}

Va retourner quelque chose comme :

Je suis le processus père et mon fils est le : 243328
Je suis le processus fils
Exemple plus complexe
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>

int main (void) {
  /* La valeur de i par défault est 5 */
  int i=5;
  pid_t ret;

  /* Ce code sera exécuté uniquement sur le père */
  printf("Avant le fork() ... \n");

  /* La valeur de retour sera 0 sur le processus fils, et le pid du fils sur le processus père */
  ret = fork();

  /* Le code à partir d'ici sera exécuté sur les deux processus */
  printf("Après le fork() ... \n");

  /* Sur le processus fils, i sera multiplié par 5 */
  if(ret == 0) {
    i*=5;

  /* Sur le processus père, i sera additioné de 5 */
  } else {
    i+=5;
  }

  /* Le code ici sera exécuté sur les deux processus */
  printf("La valeur de i est: %d\n", i);

  /* On retourne la valeur de succès d'exécution ce qui va tuer les deux processus */
  return EXIT_SUCCESS;
}

Va retourner :

Avant le fork() ...
Après le fork() ...
La valeur de i est: 10
Après le fork() ...
La valeur de i est: 25

Fin d'un processus

Un processus se termine quand il n'y a plus aucune instruction à exécuter ou lorsque l'appel système exit(int) est appellé (cette fonction permet de renvoyer une valeur entière au processus père).

wait et waidpid

Un processus père peut attendre la mort de son fils à l'aide des fonctions wait() et waitpid() et peut ainsi récupérer l'entier retourné par le exit(int) du fils.

La fonction wait() va simplement attendre la mort d'un fils (peu importe lequel) tandis que la méthode waitpid() va attendre la mort d'un processus fils déterminé.

Les fonctions wait et waitpid retourne le pid du fils, il faut donc passer le pointeur d'une variable en argument pour récupérer les valeurs. Voici un exemple :

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>

int main(void) {
  char chaine[100+1];
  int compteur = 0;
  pid_t pid_fils;

  /* On crée un nouveau processus */
  switch (fork()){
    /* Si le résultat est -1 c'est qu'il y a eu un problème */
    case -1:
      printf("Le processus n'a pas été créé.");
      exit(-1);

    /* Si on est le processus fils, on demande d'entrer une chaine de caractères */
    case 0:
      printf("Entrez une chaine de caractères : ");
      scanf("%100[^\n]%*c", chaine);

      /* On retourne la longueur de la dite chaine en exit */
      exit(strlen(chaine));

    /* Si on est le processus père, on attends la mort du fils et on récupère la sortie du exit dans une variable */
    default:
      /* On stoque le retour du exit dans une variable ainsi que le PID du fils */
      pid_fils = wait(&compteur);
      /* On extrait la longueur de la chaine depuis la sortie du wait avec WEXITSTATUS */
      printf("Enfant %d est mort. Compteur = %d", pid_fils, WEXITSTATUS(compteur));
  }

  return EXIT_SUCCESS;
}

execl

execl permet d'avoir de charger un autre dans le processus, une fois cette fonction execl exécuté le code du processus remplacé est perdu.

2023-10-03_11-41-50_screenshot.png

La fonction prends en paramètre, deux choses :

Voici un exemple d'execl :

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(void) {
  /* On crée un nouveau processus avec fork() */
  switch(fork()) {
    /* Si fork retourne -1 c'est qu'il y a eu un problème */
    case -1: printf("Erreur fork()\n");
             exit(-1);

    /* Si fork retourne 0 c'est que c'est le processus fils, on va donc exécuter la commande ls avec execl */
    case 0: printf("Je suis le fils\n");
            /* Execl va lançer la commande "ls -l" */
            /* Le premier paramètre est le chemin vers le programme */
            /* Le deuxième paramètre est le chemin vers le programme qui va être passé en argument */
            /* Le troisième paramètre est le flag "-l" qui sera passé en argument */
            /* Le NULL termine la liste des arguments */
            if(execl("/run/current-system/sw/bin/ls", "/run/current-system/sw/bin/ls", "-l", NULL)) {
              /* Si le execl retourne -1, c'est qu'il y a eu une merde */
              printf("Erreur execl()\n");
              exit(-2);
            }
            printf("Ce code ne sera jamais exécuté car il est après le execl");

    /* Pour le processus père, on va simplement attendre que le fils ai terminé */
    default: wait(NULL);
             printf("Mon fils a terminé\n");
  }
  return EXIT_SUCCESS;

  /* Le switch n'a pas besoin de break car dans tous les cas, cela se fini par un exit, il ne peut donc rien y avoir après */
}

Choix des processus

Le scheduler du système d'exploitation doit sélectionner les processus à démarrer pour maximiser l'utilisation du CPU (généralement entre 40% et 90%) pour avoir un débit (le nmobre de processus terminés par unité de temps) important (si les processus sont trop long, le début sera faible).

Algorithmes

Choix de l'algorithme

Il n'y a pas un seul bon algorithme car chaque algorithme sert à remplir un but précis.

On peut évaluer ces algorithmes selon une certaine utilisation en utilisant des modèles mathématiques, des simulations, des implémentations et des tests.

Quel algorithme utilisé dans l'OS ?

Sous Windows, c'est un système à 32 niveaux de priorités (préemptif).

Linux en revanche utilise un autre système de scheduling appelé CFS, vous pouvez en apprendre plus dans cette vidéo.


Revision #9
Created 27 September 2023 13:44:33 by SnowCode
Updated 2 January 2024 09:42:24 by SnowCode