4MMPS Exit

De Ensiwiki
Aller à : navigation, rechercher
AttentionCette page est maintenue uniquement par les enseignants. Afin de ne pas perturber le déroulement des cours, elle n'a pas vocation à être modifiée par les élèves. Mais si vous avez des modifications à proposer, merci d'en discuter ou d'envoyer un e-mail aux auteurs de la page (cf. historique)
Mycomputer.png  Deuxième Année  CDROM.png  Informatique 
Fleche haut.png

Vous avez certainement remarqué que tous les exemples de processus écrits dans les séances précédentes ne se terminaient jamais (le code était toujours entouré d'une boucle infinie), ce qui n'est pas très réaliste.

Terminaison des processus

Dans cet exercice, on va permettre aux processus de se terminer. Par exemple, on pourra écrire un processus comme suit :

void proc1(void) {
    for (int i = 0; i < 2; i++) {
        printf("[processus 1] pid = %i\n", mon_pid());
        dors(2);
    }
}

Ce processus va donc afficher deux fois sa trace, en s'endormant deux secondes entre les affichages (et à la fin), puis se terminer.

Une fois un processus terminé, il ne doit plus jamais s'exécuter : il faut donc indiquer à l'ordonnanceur de ne plus jamais sélectionner son pid, ce qui n'est pas très difficile (un booléen vivant devrait faire l'affaire).

Néanmoins, la terminaison d'un processus comprend un problème caché plus complexe : lorsque le flot d'exécution du processeur atteint la fin de la fonction proc1 dans l'exemple ci-dessus, il va executer l'instruction ret générée par le compilateur et donc dépiler l'adresse où continuer son exécution au sommet de la pile. Or cette case contient vraisemblablement n'importe-quoi à ce moment de l'exécution du processus...

Une première solution naïve consiste à ajouter systématiquement un appel explicite à la fonction chargée de gérer la terminaison des processus (cette fonction s'appelle exit dans les systèmes Unix mais comme il s'agit d'un nom de fonction reservé par GCC, vous devez l'appeler autrement) à la fin de la fonction principale de chaque processus (vous pouvez commencer par faire cela pour mettre au point la fonction de terminaison).

Une autre façon plus élégante consiste à initialiser le sommet de pile de chaque processus avec l'adresse d'une fonction gérant la terminaison de celui-ci. On rappelle qu'on doit toujours copier l'adresse de début de la fonction dans la pile avant le premier changement de contexte (il suffit de décaler cette adresse pour qu'elle soit sous le sommet de pile).

La fonction de terminaison doit évidemment marquer le processus comme étant mort, mais aussi appeler la fonction d'ordonnancement pour passer la main à un processus vivant. Comme les piles sont allouées statiquement, vous n'avez pas à les libérer (resistez aussi de toutes vos forces à la tentation de « vider » la pile en copiant des 0 dedans, ça ne sert strictement à rien à part ralentir le système...).

Evidemment, le processus idle ne doit jamais se terminer !

Création dynamique de processus

Maintenant que les processus peuvent se terminer, il est intéressant de pouvoir en créer dynamiquement (sinon, on va rapidement se retrouver avec un système qui fait idle tout le temps).

En pratique, il n'y a quasiment rien à changer pour permettre à un processus d'appeler lui-même la fonction de création de processus que vous avez écrit précédement. La seule chose a vérifier concerne l'allocation d'un pid au nouveau processus :

  • s'il y a déjà N processus vivants dans le système (en comptant idle), il n'est pas possible de créer un nouveau processus, il faut donc renvoyer un code d'erreur ;
  • s'il y a moins de N processus vivants dans le système, il faut récupérer un pid libre (i.e. jamais alloué ou celui d'un processus terminé).

Si on récupère le pid d'un processus qui a existé mais est maintenant terminé, on va aussi récupérer sa structure de données associée (sa pile, son espace de sauvegarde des registres, etc.). Il faut donc que la structure soit bien ré-initialisée (si vous avez écrit une fonction de création de processus propre, ça ne doit poser aucun problème).

A vous de créer des tests significatifs pour vérifier que la terminaison et la re-création des processus se passent bien.

Pour aller plus loin

Si vous avez fini en avance, vous pouvez étendre votre système comme bon vous semble, par exemple en permettant la gestion d'une ressource partagée (e.g. une mémoire tampon avec un système de lecteurs-rédacteurs, etc.) en implantant les outils de synchronisation adaptés, comme vu dans le cours de SEPC.