TD 5 : Redirections, Tubes et Mémoire Partagée

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

0.  Mise à jour

N'oubliez pas de mettre à jour votre archive en téléchargeant l'archive forum.tar.gz et en y insérant vos fichiers client.c, server.c et common.c.

1.  Affichage par page (call_pager)

Lorsque l'utilisateur demande à lire un message, notre client ne se contente pas d'écrire le contenu du message sur la sortie standard: il appelle un autre programme, le paginateur (pager), et lui fournit le contenu du message. Le paginateur affiche alors le message par page et permet à l'utilisateur de circuler facilement dans le message.

Pour effectuer cette opération, notre client utilise actuellement une fonction call_pager(nom_du_fichier):

extern int call_pager(char *filename);

qui appelle le paginateur, dont le nom est soit contenu dans la variable d'environnement PAGER, soit dans la constante PAGER de forum.h. Le paginateur ne reçoit pas d'argument, ce qui le force à lire sur son entrée standard le contenu du message. Finalement, la fonction retourne 0 si tout s'est bien passé.

Écrivez votre propre fonction call_pager pour votre client.

Nouveaux appels système à utiliser: pipe, dup.

Solution: call_pager.c

2.  Envoie d'un message au serveur (client_shm_write)

Lorsque l'utilisateur a écrit un nouveau message dans un fichier, le client doit transférer ce message au serveur pour que celui-ci le place dans le répertoire des messages. Cette opération est effectuée en utilisant un segment de mémoire partagé : au démarrage, le serveur crée un fichier partagé /forum_[login]_shm (où [login] est l'identifiant retourné par getlogin, ou contenu dans les variables d'environnement LOGNAME ou USER). Ce fichier est placé en mémoire par le client, dans un segment de mémoire partagé de taille MAX_MSG (voir common.h), puis utilisé pour y copier chaque message à transmettre.

Lorsqu'un message doit être envoyé, le client écrit dans ce segment de mémoire le contenu du fichier, préfixé par deux octects indiquant la taille du message, en commençant par l'octect de signification moindre (LSB, Least Significant Byte first).

Pour effectuer cette tâche l'envoi du message, le client appelle une fonction client_shm_write(filename):

extern int client_shm_write(char *filename);

qui écrit le contenu du fichier filename dans la mémoire partagée, et retourne 0 lorsque tout s'est bien passé, et une valeur négative sinon. Vous allez redéfinir cette fonction pour votre client.

Vous ferez cependant attention à vérifier systématiquement que filename n'est pas NULL. En effet, la fonction client_shm_write est appelée au début et à la fin du programme avec un argument NULL pour respectivement initialiser puis terminer la mémoire partagée.

Vous pourrez, dans une première version, effectuer tout le travail au moment où un fichier est fourni en argument. Dans une seconde version, vous pourrez profiter des phases d'initialisaton et de terminaison pour diminuer le travail effectué à chaque appel.

Appels systèmes à utiliser: mmap, shm_open, msync, munmap. On pourra par ailleurs profiter de l'option -v du serveur pour obtenir quelques informations sur ce qu'il lit dans la mémoire partagée.

Solution: client_shm_write.c

3.  Réception d'un message d'un client (server_shm_read)

Le serveur utilise symmétriquement une fonction server_shm_read(int last_num):

extern int server_shm_read(int last_num)

pour lire les messages que les clients écrivent dans la mémoire partagée. Comme pour le client, cette fonction est appelée une fois à l'initialisation et à la terminaison du serveur, avec comme argument -1. A l'initialisation, le serveur crée le fichier de partage qui est utilisé par les clients, et le place immédiatement en mémoire.

Définissez la fonction server_shm_read de votre propre client.

4.  Recherche dans les messages (search_articles)

Le client permet maintenant de rechercher des mots dans les messages. En utilisant la commnade f mot, le client affiche, pour chaque message contenant le mot mot, le numéro du message et la première ligne contenant ce mot (ou les 100 premiers caractères).

Pour effectuer cette opération, le client appelle une fonction search_articles(directory, mot):

extern void search_articles(char *directory, char* mot)

qui scanne le répertoire directory pour trouver des articles(fichiers dont le nom est un nombre), et appelle la commande externe grep sur chaque article trouvé. Il utilise alors le résultat et la sortie de la commande pour afficher le résultat pour l'article traité.

Ecrivez votre propre fonction search_articles dans votre client.