Ce travail pratique vise les objectifs suivants :
Dans ce projet, vous devrez implémenter setrFS, un système de fichiers en espace utilisateur. Plus spécifiquement, ce système de fichiers doit offrir une couche d’abstraction pour un serveur web (HTTP) : si le serveur contient les fichiers “a.txt”, “b.cpp” et “c.jpg” dans la racine de son serveur HTTP, alors un ls
à la racine de votre système de fichier doit présenter ces trois mêmes fichiers. Pareillement, ouvrir un de ces fichiers (par exemple en utilisant cat
) doit, de manière transparente à l’utilisateur, télécharger le fichier et le livrer à l’utilisateur comme si c’était un fichier local.
Le projet doit être en mesure d’effectuer ses traitements en parallèle; autrement dit, si deux processus distincts ouvrent deux fichiers (différents ou non), votre système de fichier doit pouvoir les télécharger et les livrer en même temps. L’architecture générale du projet est présentée dans la figure suivante.
Conceptuellement, le projet consiste en deux programmes roulant en tant que daemon sur le Raspberry Pi. Le premier est un serveur de téléchargement : il offre une interface sur un socket Unix qui permet à n’importe quel autre processus de demander le téléchargement d’un fichier situé sur une machine distante. Comme le téléchargement d’un fichier peut potentiellement être long, ce serveur recourt à du multiprocessing, c’est-à-dire que chaque requête est traitée par un processus différent. Cela permet une parallélisation intrinsèque des téléchargements.
Ce serveur possède une interface très simple. Afin de pouvoir réellement mettre en place un système de fichiers, il est nécessaire de fournir une implémentation plus formelle des différents appels systèmes (open, read, stat, etc.) utilisés pour explorer une arboresence et lire des fichiers. C’est la tâche du second programme, qui est un système de fichiers en espace utilisateur utilisant la librairie fuse.
Cette manière de découper le problème entre, d’une part, un programme capable d’effectuer des requêtes HTTP pour transférer localement un fichier, mais n’offrant qu’une interface limitée, et, d’autre part, un programme offrant une interface de système de fichiers complète, mais incapable de récupérer un fichier autrement que par une interface simple, confère une grande polyvalence à ce système. Par exemple, si l’on souhaitait plutôt avoir un système de fichiers offrant une couche d’abstraction à un serveur FTP, il suffit de remplacer le daemon de téléchargement, l’implémentation du daemon offrant le système de fichiers à proprement parler restant exactement la même.
Attention à ne pas mettre à jour le système (n’exécutez pas un apt-get upgrade)!
Les prérequis logiciels sont déjà installés pour vous, donc normalement il n’y a aucun changement à faire.
Pour information, les dépendances sont les suivantes :
Ces librairies sont déjà installées dans le disque image du cours. Elles devraient aussi avoir été synchronisées avec votre chaîne de compilation croisée lors du laboratoire 1. Vous n’avez donc normalement rien à faire. Si ce n’est pas le cas, assurez vous de copier les fichiers objets (fichiers .a et .so) et en-têtes de ces librairies sur votre ordinateur en suivant la procédure du laboratoire 1.
Les scripts fournis assument que les répertoires suivants existent et qu’ils sont accessibles en écriture : /home/pi/projects/laboratoire2
et /home/pi/projects/laboratoire2/pointdemontage
Afin de clarifier certains détails d’implémentation, nous vous fournissons une ébauche de code que vous devez utiliser. Les divers fichiers vous sont fournis dans un dépôt Git. Clonez ce dépôt sur votre ordinateur de développement et, une fois cet énoncé lu, prenez le temps d’explorer les divers fichiers, qui contiennent beaucoup de commentaires quant aux tâches précises à effectuer. Rappel : vous ne devriez pas avoir à cloner le dépôt contenant les fichiers sources sur le Raspberry Pi!
Note : comme pour le laboratoire 1, vous devez modifier les fichiers
.vscode/launch.json
et.vscode/tasks.json
pour y écrire l’adresse de votre Raspberry Pi, et ce dans chacun des sous-dossiers.
Le code source est divisé en deux dossiers qui constituent deux projets VSC distincts, serveurCurl et daemonFuse. Chacun de ces projets peut être exécuté indépendamment. Comme pour le laboratoire 1, des CMakeLists.txt sont fournis, ce qui vous permet de les compiler en utilisant la commande CMake Build
. Vous pouvez également les lancer en mode Debug. Faites attention à ne pas mélanger les fichiers des différents projets.
Note importante : les librairies cURL et fuse possèdent des dépendances qui génèrent des instructions illégales au lancement du programme. Ce phénomène est normal, mais pour éviter qu’il ne vous empêche de déboguer vos programmes, assurez-vous d’ajouter systématiquement un point d’arrêt (breakpoint) au début de votre fonction
main()
.
Pour cette première partie, vous devez implémenter un serveur de téléchargement. Ce serveur doit :
kill -s SIGUSR1 PID
, où PID est l’identifiant du processus serveur). Lorsque ce signal est reçu, le serveur doit immédiatement afficher dans la console des informations sur les connexions en cours, incluant au moins :Votre serveur doit pouvoir gérer au minimum cinq (5) connexions simultanées. Vous retrouverez dans le dossier serveurCurl du dépôt Git du laboratoire l’architecture de ce programme ainsi que l’implémentation ou l’ébauche de certaines fonctions clés. Chaque fichier contient des commentaires précis sur la tâche que vous devez remplir, prenez le temps de les lire attentivement et de vous faire une vue d’ensemble avant de vous lancer dans la programmation à proprement parler. De manière détaillée, chaque fichier remplit le rôle suivant :
Notez que le débogueur GDB n’est pas, par défaut, capable de déboguer les processus enfants lancés avec fork. Ainsi, vous ne pourrez pas suivre ce qui se passe dans les processus enfants exécutant cURL. Ce n’est toutefois pas un problème dans le contexte de ce laboratoire, puisque le code (correct) de téléchargement vous est déjà fourni.
La structure d’un daemon FUSE est beaucoup plus contrainte que le serveur de téléchargement. Afin de simplifier votre tâche, nous mettons à votre disposition une ébauche de code (daemonFuse/setrfs.c) ainsi qu’un fichier d’outils (daemonFuse/fstools.c) implémentant diverses fonctions qui vous seront utiles lors du laboratoire. Lisez ces fichiers avec attention avant de commencer à programmer, ils contiennent beaucoup d’informations utiles à l’implémentation du système de fichiers.
De manière générale, l’écriture de ce daemon consiste à implémenter les différents appels système qui composent un système de fichier. Par exemple, la procédure d’ouverture et de lecture d’un fichier devrait être la suivante :
cat
par exemple) utilise l’appel système open pour ouvrir le fichier. C’est à ce moment que vous demandez le téléchargement du fichier au serveur de téléchargement. L’appel à open sera bloquant tant que le téléchargement ne sera pas complété, ce qui est permis par le standard POSIX. Une fois le fichier téléchargé et récupéré via le socket Unix, vous devez le stocker dans la mémoire du daemon FUSE pour permettre son accès rapide. Une structure de cache potentielle sous forme de liste chaînée vous est fournie, mais vous pouvez opter pour un autre mécanisme.Notez que tous ces traitements s’effectuent de manière intrinsèquement parallèle. En effet, FUSE utilise un nouveau processus léger (thread) pour chaque appel système, ce qui signifie que plusieurs fichiers peuvent être ouverts simultanément, ou même que deux processus distincts peuvent ouvrir le même fichier (et donc partager le même cache). Assurez-vous donc de synchroniser vos accès au cache en utilisant le mutex déclaré dans les structures de données qui vous sont fournies et de ne supprimer une entrée du cache que lorsque tous les processus l’utilisant sont terminés. Pour vous aider au départ, les scripts de VSC sont configurés pour passer l’option -s
à votre programme pour requérir un fonctionnement single-threaded, mais vous devez supporter le mode normal (multithread) pour l’évaluation. Pour tester ce cas, une fois votre programme débogué en mode single-thread, vous pouvez le lancer directement à partir d’un terminal SSH.
Afin de vous permettre de tester votre code, un serveur HTTP a été mis en place à l’adresse http://wcours.gel.ulaval.ca/2020/h/GIF3004/default/labo2/. Cette URL pointe vers un dossier contenant plusieurs fichiers de diverses tailles allant de 1 Ko à 100 Mo. La liste des fichiers est donnés dans le fichier index.txt.
Le fichier md5sums.txt contient quant à lui la somme MD5 de chaque fichier du répertoire (sauf lui-même), pour faciliter la validation. Par exemple, si vous voulez vérifier que votre programme est en mesure de télécharger sans erreur le fichier file1Mo, utilisez simplement la commande md5sum file1Mo
. Le résultat devrait être le même que celui contenu dans le fichier md5sums.txt; si ce n’est pas le cas, c’est qu’il y a une erreur dans votre programme et que vous n’êtes pas en mesure de restituer le fichier dans son intégrité.
N’oubliez pas que votre système de fichiers doit pouvoir gérer plusieurs requêtes simultanées. À titre d’exemple, télécharger le fichier file100Mo devrait demander plusieurs secondes vu la taille de ce dernier; vous devriez être en mesure d’afficher un autre petit fichier (comme fichier.cpp ou logo.png) presque instantanément, sans devoir attendre la fin du téléchargement du gros fichier!
Afin de simplifier la tâche à effectuer, vous pouvez assumer que :
Le projet doit être implémenté en C ou C++. Le C étant le langage historique de Unix et utilisé dans la norme POSIX, son utilisation est recommandée, mais vous pouvez utiliser des éléments C++, qui peuvent se révéler utiles dans certains occasions, notamment pour les structures de données avancées. Aucune fonction ou structure de données fournie n’utilise de fonctionnalités du C++.
Pour tester votre système de fichiers, nous vous conseillons d’utiliser d’abord les outils en ligne de commande (ls, cp, cat, md5sum, etc.) plutôt qu’un explorateur de fichier graphique, la raison étant qu’un tel explorateur fait énormément d’appels au système de fichiers, ce qui rend le débogage plus délicat au départ. Toutefois, une fois le programme stabilisé, une telle application pourrait constituer un très bon test de robustesse! Ceci exige cependant l’installation d’un environnement graphique, ce qui n’est pas le cas avec l’image du cours pour le Raspberry Pi.
Un excellent outil pour déboguer votre système de fichier est le programme strace. Ce programme permet de tracer tous les appels système effectués par un processus, y compris, donc, ceux reliés à l’ouverture d’un fichier ou d’un dossier. S’il y a une erreur dans une de vos fonctions, il vous sera ainsi beaucoup plus simple de déterminer laquelle (plutôt que de simplement voir un processus bloqué).
Par défaut, le système de fichiers FUSE se lance à l’arrière-plan, c’est-à-dire qu’il se déconnecte du terminal courant. Si c’est, de manière générale, un comportement souhaitable, il est moins utile dans un contexte de débogage. Pour éviter cela, les scripts de VSC sont configurés de manière à le lancer avec l’option -f
, ce qui le force à rester à l’avant plan. De même, FUSE requiert un point de montage (mountpoint) comme premier argument. Par défaut, celui-ci est un dossier nommé pointdemontage dans le même répertoire que l’exécutable. Ce point de montage peut cependant être n’importe quel dossier vide sur lequel vous avez les droits d’écriture. Si votre programme plante et que vous êtes incapable de le relancer, assurez-vous que ce point de montage est bien démonté en utilisant la commande fusermount -u mon_point_de_montage
sur votre Raspberry Pi.
N’oubliez pas que le projet est constitué de deux exécutables distincts. Par conséquent, ceux-ci doivent être lancés ensemble pour que le système soit complètement fonctionnel. Vous pouvez pour cela utiliser deux ordinateurs, mais aussi ouvrir deux fenêtres de VSC : les deux exécutables étant dans des dossiers distincts, ils sont considérés comme des projets différents par VSC. Si vous avez terminé et débogué un des exécutables, vous pouvez également l’exécuter à distance via SSH et le laisser fonctionner en arrière-plan sur le Raspberry Pi.
Comme dans le laboratoire 1, les scripts sont configurés de manière à activer le report des avertissements par le compilateur. De plus, le code fourni est exempt d’erreurs et d’avertissements. Vous devez vous assurer que votre code compile sans erreur ET sans avertissement.
Attention : les avertissements ne sont affichés par GCC que lorsque vous compilez effectivement un fichier. Si vous ne le modifiez pas et relancez la compilation, ces avertissements « disparaîtront » puisque GCC ne tentera même pas de recompiler les fichiers fautifs. Assurez-vous donc de toujours nettoyer (
CMake Clean
) votre environnement de compilation avant de compiler pour retirer les avertissements.
Ce travail doit être réalisé en équipe de deux, la charge de travail étant à répartir équitablement entre les deux membres de l’équipe. Aucun rapport n’est à remettre, mais les deux équipiers doivent être en mesure individuellement d’expliquer leur approche et de démontrer le bon fonctionnement de l’ensemble de la solution de l’équipe du laboratoire. Cette évaluation sera faite lors de la séance de laboratoire du 21 février 2020, et les deux équipiers doivent y être présents. Si vous ne pouvez pas vous y présenter, contactez l’équipe pédagogique du cours dans les plus brefs délais afin de convenir d’une date d’évaluation alternative. Ce travail compte pour 10% de la note totale du cours.
Le barême d’évaluation détaillé sera le suivant (laboratoire noté sur 20 points) :
man nom_de_la_commande
.