Précédent Table des matières Suivant

2  Implantation d’un gestionnaire de travaux


Exercice 2
 (Structures de données pour les jobs et fonctions associées)   La gestion des différents jobs nécessite de maintenir des informations sur chaque job. Un job est identifié par son pid ou bien son job id, ces numéros lui sont attribués à son lancement. Ainsi, par exemple, le job xeyes de la session shell montrée précédemment a pour pid 503 et pour job id 2. De plus, à tout moment, on a besoin de savoir si le job est exécuté en arrière-plan (BG), en avant-plan (FG) ou s’il est stoppé (ST).

Proposez des structures de données pour représenter toutes ces informations. De plus, proposez un ensemble de fonctions permettant de manipuler ces structures.

Traitants de signal

Il est nécessaire de rediriger les signaux SIGINT, SIGTSTP et SIGCHLD envoyés au mini-shell par le système. Un signal SIGINT est envoyé à chaque fois que l’utilisateur fait un Ctrl+C au clavier. Un signal SIGTSTP est envoyé lors d’un Ctrl+Z. Enfin le signal SIGCHLD est envoyé à chaque fois que l’exécution d’un programme lancé dans le mini-shell a terminé ou a été bloquée.


Exercice 3
   Il est nécessaire d’installer des traitants de signaux pour SIGINT, SIGTSTP et SIGCHLD. Donnez l’implémentation de la fonction sigaction_wrapper().
typedef void handler_t(int);
int sigaction_wrapper(int signum, handler_t *handler);
qui permet d’associer un traitant à un signal donné.

Exercice 4
   Donnez l’implémentation du traitant de signal pour SIGINT :
void sigint_handler(int sig);
Ce traitant redirige le signal vers le processus qui est en avant-plan afin de provoquer sa terminaison.

Exercice 5
   Donnez l’implémentation du traitant de signal pour SIGTSTP :
void sigstp_handler(int sig);
Ce traitant redirige le signal vers le processus qui est en avant-plan afin de le suspendre.

Exercice 6
   Donnez l’implémentation du traitant de signal pour SIGCHLD :
void sigchld_handler(int sig);
Ce traitant recherche tous les jobs terminés ou stoppés. Il détermine si le job a été stoppé, terminé par un signal ou a terminé normalement son exécution. Selon les cas, les structures de données sont mises à jour de façon appropriée et un message est affiché sur la sortie standard pour renseigner sur l’état du job.

Organisation du code du gestionnaire de travaux

Les fichiers sources d’un squelette de l’implantation d’un gestionnaire de travaux vous sont fournis.

Vous y trouverez :

A priori, vous n’avez à modifier que les fichiers cmd.c et sighandlers.c. Les squelettes des fonctions à implémenter sont déjà donnés ; aucune autre fonction n’est nécessaire.


Le mini-shell peut fonctionner en mode verbose (mshell -v) ce qui permet d’avoir des informations sur les traitants et fonctions sollicités, etc. Une variable globale verbose est prévue à cet effet. Pensez à l’utiliser ! Le fichier jobs.c contient un exemple de son utilisation, à vous de voir selon les situations quelles informations afficher.

Comment tester les traitants de signal ?

Pour tester les premières fonctionnalités du mini-shell, il est proposé ci-dessous quelques idées.

Pour réaliser ces tests il est nécessaire que la fonction waitfg soit implémentée. Il est mieux aussi que dans la fonction sigchld_handler il y ait impression à l’écran de messages explicites en mode verbeux afin de bien comprendre ce qui se passe.

Lancer le mini-shell en mode verbeux puis:

Module de description des jobs

Petit extrait de jobs.h, module qui vous est fourni pour définir la structure correspondant à chaque job en cours d’exécution et manipuler cette structure.

struct job_t {                  /* The job struct       */
    pid_t jb_pid;               /* job PID              */
    int jb_jid;                 /* job ID [1, 2, ...]   */
    enum jstate jb_state;       /* UNDEF, BG, FG, or ST */
    char jb_cmdline[MAXLINE];   /* command line         */
};

/* maxjid - Returns largest allocated job ID */
extern int jobs_maxjid();

/* addjob - Add a job to the job list */
extern int jobs_addjob(pid_t pid, int state, char *cmdline);

/* deletejob - Delete a job whose PID=pid from the job list */
extern int jobs_deletejob(pid_t pid);

/* fgpid - Return PID of current foreground job, 0 if no such job */
extern pid_t jobs_fgpid();

/* getjobpid  - Find a job (by PID) on the job list */
extern struct job_t *jobs_getjobpid(pid_t pid);

Commandes du mini-shell

Le mini-shell offre un certain nombre de commandes :


Exercice 7
   La commande fg est invoquée soit par fg pid soit par fg jid.

Le prototype de la fonction implémentant la commande fg est le suivant :

void do_fg(char **argv);

argv contient la chaîne de caractères correspondant à la commande tapée dans le mini-shell, comme par exemple "fg %1". Une fonction treat_argv() est mise à votre disposition pour chercher un job dans la liste des travaux en fonction d’un pid ou d’un jid. Son prototype est :

struct job_t * treat_argv(char **argv);

La valeur NULL est retournée si aucun job n’a été trouvé.


Lorsqu’un job est exécuté en avant-plan, l’utilisateur n’a pas la main. Pour pouvoir implémenter ce comportement, écrivez une fonction

void waitfg(pid_t pid)

qui bloque tant que le job pid n’est pas mis en arrière-plan ou terminé.

La fonction do_fg() envoie le signal approprié au job qui doit être passé en avant-plan, met à jour les informations associées au job et bloque tant que le job n’est pas passé en arrière-plan. Donnez son implémentation.

Les commandes bg, stop et kill sont invoquées sur le même modèle que fg et sont respectivement implémentées par :

void do_bg(char **argv);
void do_stop(char **argv);
void do_kill(char **argv);

Donnez leurs implémentations.


Exercice 8
   La commande exit permet de sortir du mini-shell. Le prototype de la fonction qui implémente exit est le suivant :
void do_exit();
Inspirez-vous du comportement du shell que vous utilisez habituellement (cette remarque est d’ailleurs aussi valable pour toutes les autres commandes). Par exemple, que se passe-t-il lorsque vous faites un exit dans un shell alors qu’un job de ce shell est arrêté ?

Exercice 9
   La commande jobs affiche l’état de tous les jobs lancés dans le mini-shell, voyez l’exemple donné en début de document.

La fonction qui implémente cette commande a pour prototype :

void do_jobs();

Implémentez cette fonction.


Précédent Table des matières Suivant