:: Enseignements :: Licence :: L3 :: 2008-2009 :: Programmation système en C ::
![[LOGO]](http://igm.univ-mlv.fr/ens/resources/mlv.png) | Projection en mémoire (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 n'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 - Premier clap
-
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 - Bout d'essai
Écrire maintenant un programme prenant deux arguments et effectuant une
recopie de fichier grâce à mmap. Attention, le fichier d'origine
peut faire n'importe quelle taille cette-fois ci (truc et 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 - Ça tourne
Passons aux choses sérieuses! écrire un programme arbitre qui
prend en argument 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,
puisremplir 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
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 - Promotion
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
USR1 à l'arbitre (dont le PID est dans le fichier
d'échange, n'est-ce pas). L'arbitre envoie à ce moment là un
signal USR1 à l'utilisateur si personne d'autre n'est en
train d'accéder à la zone d'échange. Sinon il envoie USR2. Si
l'utilisateur a reçu USR1, il peut alors lire et écrire
des messages, puis il envoie USR2 à l'arbitre pour
indiquer qu'il a fini. Si l'utilisateur avait reçu USR2
(signifiant "ressource occupée"), il a le droit de réessayer régulièrement
d'envoyer USR1 (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).
© Université de Marne-la-Vallée