Simulation d'un système informatique avec programme stocké : Initialisation des processus

Dans cette phase, nous finalisons la modélisation d'un noyau système en ajoutant la gestion des processus. Nous partons du principe qu'un processeur x86 avec interruption horloge est déjà simulé et les variables système initialisées. Il s'agit maintenant de définir les structures de contrôle de processus (PCB) et de coder la commutation de contexte.

Définition des structures de contrôle de processus

Le noyau Linux utilise la structure struct task_struct pour le PCB. Dans notre implémentation mykernel, nous créons un fichier d'en-tête mykernel/mypcb.h pour définir le bloc de contrôle. Ce dernier contient des champs essentiels comme le pointeur d'instruction (IP) et le pointeur de pile (SP) pour chaque instance de processus.

Fonction d'initialisation du système

La routine d'initialisation Linux se trouve dans init/main.c:start_kernel(). Nous adaptons cela dans mymain.c:my_start_kernel() pour servir de point d'entrée à notre noyau. L'initialisation consiste à régler le pointeur d'instruction de chaque processus (contexte.ip) sur l'adresse de la fonction de traitement my_process, et le pointeur de pile (contexte.sp) sur l'adresse finale de la pile du processus, soit &table_processus[pid].pile[TAILLE_PILE_NOYAU-1].

Séquence assembleur pour lancer le premier processus

asm volatile(
  "movl %1, %%esp\n\t"
  "pushl %1\n\t"
  "pushl %0\n\t"
  "ret\n\t"
  "popl %%ebp\n\t"
  :
  : "c" (table_processus[pid].contexte.ip), "d" (table_processus[pid].contexte.sp)
);

Détail des opérations :

  • L'attribut volatile force la relecture en mémoire à chaque accès, sans optimisation par le compilateur.
  • movl %1, %%esp : charge la valeur du pointeur de pile dans le registre ESP.
  • pushl %1 : comme la pile est vide, initialise le registre EBP avec la même adresse que le sommet de pile.
  • pushl %0 : place l'adresse IP du processus sur la pile, faisant évoluer ESP.
  • ret : transfère le contrôle vers l'adresse IP, lançant l'exécution de my_process.
  • popl %%ebp : instruction non exécutée, présente pour maintenir la symétrie des opérations de pile.

Fonction d'ordonnancement des processus

L'équivalent de la fonction schedule() du noyau Linux est implémenté dans myinterrupt.c sous le nom my_schedule(). Nous traitons deux cas distincts : le processus suivant a déjà été exécuté, ou il s'agit d'une première exécution.

Processus suivant déjà démarré

asm volatile (
  "pushl %%ebp\n\t"
  "movl %%esp, %0\n\t"
  "movl %2, %%esp\n\t"
  "movl $1f, %1\n\t"
  "pushl %3\n\t"
  "ret\n\t"
  "1:\t"
  "popl %%ebp\n\t"
  : "=m" (courant->contexte.sp), "=m" (courant->contexte.ip)
  : "m"  (suivant->contexte.sp), "m"  (suivant->contexte.ip)
);

Explication du mécanisme :

  • Sauvegarde du contexte du processus actuel : on conserve EBP sur la pile et on stocke ESP dans le PCB.
  • Changement de contexte : on charge le pointeur de pile du processus suivant, on mémorise l'adresse de reprise (étiquette 1:) dans son PCB, et on exécute un saut vers son code via ret.
  • L'étiquette 1: marque le point de reprise pour ce processus lors d'une prochaine commutation.

Première exécution du processus suivant

asm volatile(
  "pushl %%ebp\n\t"
  "movl %%esp, %0\n\t"
  "movl %2, %%esp\n\t"
  "movl %2, %%ebp\n\t"
  "movl $1f, %1\n\t"
  "pushl %3\n\t"
  "ret\n\t"
  : "=m" (courant->contexte.sp), "=m" (courant->contexte.ip)
  : "m" (suivant->contexte.sp), "m" (suivant->contexte.ip)
);

Variantes par rapport au cas précédent :

  • La restauration de EBP utilise directement l'adresse du sommet de pile du nouveau processus (pile vide).
  • Les étapes de commutation restent similaires, initialisant ainsi un nouveau contexte d'exécution.

Étiquettes: x86 PCB processus assembleur ordonnancement du processeur

Publié le 22 juin à 20h56