TD 4 : Processus - Signaux

Introduction      FAQ   TD 1   TD 2   TD 3   TD 4   TD 5   TD 6   TD 7   TD 8

0.  Mise à jour

A ce stade du projet, nous allons devoir effectuer une mise à jour complète du projet. Cette tâche est un peu plus compliquée que les mises à jour précédentes. Voici la façon de faire :

  1. Téléchargez la nouvelle archive forum.tar.gz .
  2. Décompressez la dans un répertoire temporaire que vous aurez créé pour l'occasion (sinon, vous risquez d'effacer les fonctions que vous avez écrites lors des TP précédents) :

    tar -zxvf forum.tar.gz

  3. Copiez vos fichiers common.c, client.c et server.c dans le répertoire contenant les fichiers de l'archive.
  4. Modifiez ces trois fichiers pour en retirer tout le code que vous n'avez pas écrit. En effet, ce code a été déplacé dans les fichiers common_error.c, client_main.c et server_main.c. Si vous avez un doute, vous pouvez lire ces fichiers pour vérifier que le code que vous supprimez y est déjà.
  5. Dans les TP, vous ne devrez modifier que les fichiers common.c, client.c et server.c en fonction des questions demandées.

Voici les modifications que vous trouverez dans le nouveau projet:

1.  Terminaison propre (clean_client et clean_server)

A la terminaison d'un programme, il peut s'avérer important de libérer un certain nombre de ressources utilisées par un programme. Par exemple, dans notre programme actuel, il faut supprimer le fichier verrou persistant créé par program_lock, qui empêche l'exécution d'une autre instance du programme. Il peut aussi être utile de supprimer certains fichiers temporaires, tels que celui utilisé pour l'envoi de messages au serveur.

Dans notre cas, nous utilisons respectivement les fonctions clean_client() et clean_server() qui sont appelées à la terminaison respectivement du client et du serveur.

extern void clean_client();
extern void clean_server();

Redéfinissez ces fonctions dans votre programme pour ne plus utiliser celles déjà fournies par libforum*.a. Pour l'instant, que pouvez-vous libérer comme ressources dans ces fonctions ?

Solutions: clean_client.c , clean_server.c

2.  Terminaison sur signaux (client_signals)

Sous Unix, un processus doit être prêt à recevoir un certain nombre de signaux de son environnement (SIGQUIT, SIGPIPE, SIGINT, etc...). Si ce n'est pas le cas, il peut se comporter de façon inattendue, en fonction du comportement par défaut de ces signaux.

Dans notre forum, le client utilise une fonction client_signals(),

extern void client_signals();

pour spécifier que, à la réception de la plupart des signaux qui tueraient le processus, il faut afficher un message indiquant la réception du signal et terminer proprement en libérant les ressources utilisées.

Testez cette fonction en envoyant divers signaux via la commande UNIX kill à votre client.

Voici une liste des signaux que vous pouvez vouloir traiter : SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGSYS, SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2.

Fonctions à utiliser (man): signal (ou sigaction, sigemptyset et sigaddset).

Solution: client_signals.c

3.  Alarmes (server_signals)

Dans notre forum, le serveur utilise une fonction similaire à celle du client, server_signals(),

extern void server_signals();

pour gérer les signaux. Contrairement au client, qui termine proprement directement à la réception d'un signal, le serveur se contente de modifier une variable globale sig_end, que le serveur teste régulièrement, sachant qu'il doit terminer si cette variable est différente de zéro.

De plus, le serveur utilise une variable globale minutes pour compter le nombre de minutes depuis le lancement du programme. La valeur de cette variable est utilisée pour déclencher un remove_expired() (toutes les minutes), et la terminaison du serveur (après 30 minutes).

Ecrivez votre fonction server_signals du serveur, correspondant à ce comportement. Vous pouvez utiliser la fonction alarm pour déclencher un signal toutes les minutes.

Solution: server_signals.c

4.  Blocage des signaux SIGCHLD (block_sigchld et unblock_sigchld)

4.1  Gestion asynchrone des SIGCHLD

Une technique très courante consiste à placer une boucle effectuant des waitpid dans le récepteur de signaux SIGCHLD. Ceci permet d'éviter de laisser des processus zombie dans le système.

Modifiez la fonction client_signals précédente pour utiliser cette technique. Il se peut qu'un seul signal SIGCHLD soit reçu pour plusieurs processus fils: comment faut-il gérer ce problème ?

4.2  Utilisation du blocage des signaux

Quel risque court maintenant votre programme ? Etudiez en particulier le code de la fonction call_editor.

Pour résoudre ce problème, nous avons défini des fonctions block_sigchld et unblock_sigchld qui permettent de bloquer temporairement la réception des signaux SIGCHLD:

extern void block_sigchld(); 
extern void unblock_sigchld();

block_sigchld bloque le signal SIGCHLD, tandis que unblock_sigchld rétablit le comportement précédent de ce signal.

Modifiez call_editor pour utiliser ces fonctions et éviter le risque que la gestion asynchrone des signaux fait courir au programme.

4.3  Blocage des signaux

Définissez maintenant vos propres fonctions block_sigchld et unblock_sigchld. Utilisez pour cela l'appel système sigprocmask.