:: Enseignements :: ESIPE :: E4INFO :: 2014-2015 :: Programmation système en C ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) | mmap() |
MMAP est une abréviation pour
memory-map. En français: projection mémoire.
Plus précisément, on parle ici de projection de fichiers. L'appel système
mmap()
permet de faire correspondre une partie de la mémoire à un fichier du disque. Voici
quelques exemples d'utilisation de cette primitive:
- Chargement facile de fichiers. En effet, le fichier étant projeté en mémoire, il n'est plus nécessaire
d'utiliser des primitives comme read ou seek pour lire des fragments du fichier.
Il n'est pas non plus nécessaire de gérer des buffers, puisque le fichier est virtuellement
présent intégralement en mémoire. En réalité, le fichier n'est pas chargé entièrement,
mais page par page (comme si il était dans le swap de la machine), et chaque accès à une page
(bloc de 4 kilo-octets sur une architecture IA32) provoque le chargement de cette page
si elle n'est pas déjà en mémoire.
- Allocation mémoire efficace et simple. Lorsqu'on souhaite allouer un bloc de X octets,
on peut tout aussi bien projeter un fichier de taille X. Ceci permet de s'en remettre au
noyau pour gérer la mémoire. C'est efficace pour les blocs de moyenne et grande taille
(de plus d'une page mémoire). Par contre pour les petits blocs, c'est généralement moins
efficace que malloc, donc à éviter dans ces cas-là.
- Partage d'un segment de mémoire entre plusieurs processus. Rien n'interdit deux (ou plus)
processus de projeter le même fichier en mémoire, et d'y accéder de façon concurrente. Il
existe deux modes de projection: un dit privé, et un dit partagé. En mode partagé,
les modifications faites sur le fichier projeté sont répercutées sur le fichier réel
(et sont visibles par les autres processus partageant le fichier). En mode privé, les
modifications de la mémoire d'affectent pas le fichier (ni les autres processus).
Nous allons ici utiliser
mmap et les signaux pour construire un protocole d'échange de messages entre
plusieurs processus.
Exercice 1 - Le top 100
- Créer un fichier de 100 octets, contenant l'alphabet en majuscule, répété comme un motif (ABCD...XYZABCD... etc.).
- Projeter la totalité de ce fichier en mémoire. Faire une projection privée et en
lecture seule pour l'instant.
- Afficher le contenu du fichier projeté (en utilisant write) de la façon la plus simple
possible (ceux qui utiliseront un buffer seront fouettés publiquement pour n'avoir pas lu l'introduction du TD).
- Que se passe-t-il si on essaie de lire les 1000 premiers octets du fichier ? Les 10000 premiers octets ?
Essayez et interprétez.
Exercice 2 - Copies à la carte
- Écrire maintenant un programme prenant deux arguments, et effectuant une recopie de fichier grâce à un mmap
sur le fichier source et un write.
- Même question, mais cette fois-ci, la copie se fera en mappant le fichier source et le fichier destination, puis en
faisant une copie entre les deux zones mémoires.
Attention, le fichier d'origine peut faire n'importe quelle taille cette-fois ci (truc & astuce: stat et memcpy
sont tes amis). Note: pour faire une projection "en écriture", il faut avoir ouvert le fichier écriture,
l'avoir projeté en écriture aussi, et de façon partagée (sinon les modifications ne sont pas reflétées sur le
fichier réel).
Exercice 3 - Le jeu du PID
- Écrire un programme arbitre qui prend en arguments un nom de fichier et une taille.
Le programme doit créer le fichier, lui donner la taille voulue, le projeter en mémoire, puis y
écrire son PID sur 4 octets (comme un int) au tout début, puis remplir le reste avec des zéros.
- Écrire maintenant un programme utilisateur, prenant en argument un nom de fichier et un message.
Ce programme doit ouvrir le fichier, le projeter intégralement en mémoire, lire le PID contenu au début et l'afficher;
puis il doit lire toutes les chaînes présentes dans le fichier, et ajouter le message passé comme argument de
la ligne de commande au bout des chaînes déjà existantes. On considère que les chaînes sont séparées par des zéros.
Ainsi, au bout de quelques messages, le fichier pourra contenir ceci : "XXXXJe crois que nous avons affaire à un serial
killer.\0Un quoi?\0Un serial killer.\0\0\0\0\0", avec ensuite autant de \0 que nécessaires pour
compléter le fichier. XXXX est le PID de l'arbitre sur 4 octets.
- Tester le programme en ajoutant quelques messages. Sauter de joie en levant les deux bras en l'air et
en s'exclamant d'un air satisfait : "Yes!" Puis, réfléchir aux problèmes de ce "protocole". Si vous ne voyez pas
ce qui peut arriver, modifiez le programme utilisateur pour qu'il copie les octets 1 par 1, avec une attente de 1
seconde entre deux octets; puis lancez-le plusieurs fois en même temps.
Exercice 4 - Le bouquet final
- Passons aux signaux. Écrire un programme qui affiche son PID, puis met en place un gestionnaire de signaux pour
SIGUSR1, et attend la réception de ce signal (avec pause() par exemple). Lorsque le signal est
reçu, il faut afficher le PID du processus qui a envoyé le signal. Pour celà, utiliser le flag SA_SIGINFO
et un gestionnaire de signaux à trois arguments (voir la manpage de sigaction pour
le prototype exact). Tester le programme en faisant kill X depuis le shell (où X est le PID du
programme - qui a été affiché, rappelez-vous).
- Modifier arbitre et utilisateur de façon à ce qu'avant d'écrire un message,
l'utilisateur doive envoyer le signal SIGUSR1 à l'arbitre (dont le PID est dans le fichier d'échange,
n'est-ce pas). L'arbitre envoie à ce moment là un signal SIGUSR1 à l'utilisateur si personne
d'autre n'est en train d'accéder à la zone d'échange; sinon il envoie SIGUSR2. Si l'utilisateur a
reçu SIGUSR1, il peut alors lire et écrire des messages, puis il envoie SIGUSR2 à
l'arbitre pour indiquer qu'il a fini. Si l'utilisateur avait reçu SIGUSR2 (signifiant
"ressource occupée"), il a le droit de réessayer régulièrement d'envoyer SIGUSR1
(pour dire "je demande l'accès") jusqu'à ce qu'il y arrive (ou bien il peut immédiatement afficher un
message d'erreur disant "réessayez plus tard" et quitter).
Exercice 5 - Post-scriptum
Menteurs, vous n'êtes pas arrivés là en ayant fini tous les exercices de toutes les planches précédentes.
© Université de Marne-la-Vallée