Aller au menu - Aller au contenu

[Plan du site] Vous êtes ici --- > Le Site du Zéro > Les tutoriels > Non-Officiels > Programmation > C > Les fichiers mappés en mémoires (UNIX) > Lecture du tutoriel

Les fichiers mappés en mémoires (UNIX)

Vous vous apprêtez à lire un tutoriel rédigé par un membre de ce site. Malgré tout le soin que ce membre a pu apporter au tutoriel, nous ne pouvons pas garantir que les informations contenues sur cette page sont exactes à 100%. Merci de garder cela en tête lorsque vous lirez cette page ;o)
Avatar
Auteur : Nyxem
Visualisations : 1 749

Plus d'informations Plus d'informations
Ce petit tuto a pour but de vous apprendre le fonctionnement des fichiers 'mappés' en mémoire.
C'est bien beau tout ça, mais en quoi ça consiste en quoi exacement ? o_O


mapper un fichier consiste tout simplement à charger celui-ci en mémoire et réaliser des opérations dessus.
Il y a donc un gain de performance par rapport à des opérations standard.

PS : Ce tuto s'adresse aux personnes sous systèmes basés sur UNIX, donc tous les utilisateurs de unix / linux / OS X peuvent le suivre sans problème.


Sommaire du tutoriel :
Icône du chapitre

Ouverture du fichier

a) Création du fichier

On va donc commencer par créer un fichier texte que nous appellerons tout bêtement test.txt
Ajoutons-y les 10 premières lettres de l'alphabet par exemple.
ABCDEFGHIJ


b) Ouverture du fichier

Nous allons maintenant ouvrir ce fichier dans notre programme.
Ici j'ai appelé mon fichier C : memory_mapping.c

Pour ouvrir notre fichier nous allons faire appel à une fonction qui s'appelle open() et dont le prototype est le suivant :
Code : C
1
int open(const char* path, int oflag, ...);


Il ne faudra pas oublier d'inclure :
Code : C
1
#include <fcntl.h>


Les paramètres sont les suivants :
- const char* path : Une chaîne de caractères contenant le chemin où se situe votre fichier.
- int oflag : Les flags pour le mode d'ouverture du fichier
Un flag, mais qu'est kesako ? o_O

Un flag est une constante qui va nous servir à spécifier le mode d'ouverture du fichier, dans notre exemple nous voulons lire et écrire dans le fichier, le flag que nous utiliserons est donc : O_RDWR
RD pour read et WR pour write. ;)

Ici le choix du flag est très important, si par exemple vous ouvrez le fichier en lecture uniquement (O_RDONLY), la modification en mémoire sera impossible!


open() fonction renvoie -1 si elle échoue, sinon elle renvoie un entier qui est ce que l'on appelle un file descriptor sur le fichier.
M@teo21 nous a dit que pour ouvrir un fichier il fallait se servir de fopen()

Cette fois ci nous n'avons pas le choix que d'employer cette fonction, car sa valeur de retour est nécessaire par la suite pour la fonction qui mappera le fichier en mémoire.

Pour des informations plus approfondies sur open() je vous invite à aller voir la page de manuel ici


Voici donc le code avec la détection d'erreur :
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
 
int main(void)
{
   int fd; // Notre File Descriptor
   char chemin_fichier[200]; // buffer pour le chemin du fichier
   memset(chemin_fichier, 0x00, 200); // Fonction qui remplit chemin_fichier de '0', pour initialiser
   printf("Chemin du fichier à mapper : ");
   scanf("%s", chemin_fichier); // Saisie du chemin du fichier
   if (-1 == (fd = open(chemin_fichier, O_RDWR))) // Ouverture en lecture et écriture
   {
      perror("open() "); // perror affichera un message indiquant la cause de l'erreur
      exit(EXIT_FAILURE); // sortir du programme
   }
   return EXIT_SUCCESS;
}


Voilà, si tout c'est bien passé notre fichier est ouvert, nous allons donc récupérer sa taille.

Récupération de la taille du fichier

Nous avons besoin de connaître la taille du fichier pour savoir combien de mémoire il faut allouer pour mappper le fichier.

Là encore nous allons utiliser une fonction nécessitant des #include supplémentaires.
Code : C
1
2
3
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>


Le prototype de la fonction est le suivant :
Code : C
1
int stat (const char* restrict path, struct stat* restrict buf);


Voici une petite description des paramètres :
- const char* restrict path : Vous l'avez deviné, c'est le chemin du fichier dont vous souhaitez connaître la taille.
- struct stat* restrict buf : Un pointeur sur une structure stat qui possède entre autre un attribut stockant la taille :)

Comme open(), stat() renvoie -1 en cas d'erreur, 0 en cas de réussite, et les informations sur notre fichier stockées dans la structure.

Pour plus d'information sur stat()


Voici donc le code :
Code : C
1
2
3
4
5
6
7
8
...
struct stat buf;
if (-1 == stat(nom_fichier, &buf)) // Obtention de la taille, on passe l'adresse de buf car la fonction attend un pointeur!
{
   perror("stat() ");
   close(fd); // Fermeture du fichier
   exit(EXIT_FAILURE); // Si erreur on sort
}


Notez un appel à une fonction close() qui comme fclose() ferme un fichier ouvert en prenant comme paramètre un entier, le file descriptor.
En effet si la fonction stat() échoue, avant de sortir du programme il faut penser à fermer notre fichier!

Pour les curieux voici la définition de la structure stat :

Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
struct stat
{
dev_t st_dev;
ino_t st_ino;
mode_t st_mode;
nlink_t st_nlink;
uid_t st_uid;
gid_t st_gid;
dev_t st_rdev;
struct timespec st_atimespec;
struct timespec st_mtimespec;
struct timespec st_ctimespec;
off_t st_size;
quad_t st_blocks;
u_long st_blksize;
u_long st_flags;
u_long st_gen;
};

Mais qu'est ce donc que tous ces types bizarres ? o_O

Il s'agit pour la plupart de types standard qui ont été redéfinis par des typedef dans le fichier types.h,
ce qui explique le :

Code : C
1
#include <sys/types.h>


L'attribut qui nous intéresse, en l'occurence celui qui contient la taille du fichier (en octets), est celui-ci : off_t st_size;
</code>

Voilà, nous avons donc ouvert le fichier et nous connaissons sa taille, nous pouvons donc passer à la suite, à savoir mapper le fichier ! :)

mapper le fichier

Ca y est nous y voilà, nous allons maintenant mapper le fichier en mémoire!

Pour ça un dernier include
Code : C
1
#include <sys/mman.h>


La fonction que nous allons utiliser s'appelle mmap(), pour Memory Map.
Elle ne comporte rien moins que 6 paramètres ^^ mais ne vous inquiétez pas, rien de bien sorcier.

Voici donc son prototype :
Code : C
1
void* mmap(void* addr, size_t len, int prot, int flags, int fildes, off_t offset);

Une petite description des paramètres s'impose :)

- void* addr : un pointeur sur void pour spécifier l'adresse où commencer à mapper le fichier, nous prendrons 0.
- size_t len : La taille à allouer, size_t peut être considéré comme un unsigned int, donc la taille de notre fichier.
- int prot : Un entier pour spécifier quel type d'opérations on souhaite réaliser, sous formes de constantes symboliques, nous aurons besoin de 2 constantes : PROT_READ et PROT_WRITE.
- int flags : Un entier pour spécifier le type de mapping utilisé, nous utiliserons la constante MAP_SHARED pour indiquer que la zone de mémoire est partagée aux autres processus utilisant l'objet.
- int fildes : Le file descriptor associé au fichier, (la valeur de retour de open()).
- off_t offset : Position à laquelle le mapping va commencer, là aussi nous prendrons 0.

Pour des infos plus précises c'est ici!


La fonction renvoie un pointeur universel, ce qui signifie en clair que la valeur de retour peut être de n'importe quel type.

Voici donc le code :
Code : C
1
2
3
4
5
6
7
8
9
char* buffer;
buffer = (char*)mmap(0, (int)buf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // Mappage
if ((char*)-1 == buffer)
{
   perror("mmap() ");
   close(fd); // Fermeture du fichier
   exit(EXIT_FAILURE);
}
close(fd); // Fermeture du fichier

Modifier le fichier

Maintenant que notre fichier est mappé en mémoire, nous pouvons faire des opération dessus.
Je vous propose de modifier son contenu et de remplacer les 10 lettres par 10 chiffres, de 0 à 9.

Vous l'aurez deviné, une simple boucle for() suffit :)

Voici donc le code :
Code : C
1
2
3
unsigned short i;
for (i = 0 ; i < (int)buf.st_size ; i++)
        buffer[i] = i+'0'; // Remplacement (+'0' pour obtenir les chiffres ASCII)


Une fois que vous avez fini, il est très important de libérer la mémoire que nous avons allouée, il faut donc pour ça faire un appel à :
Code : C
1
int munmap(void* start, size_t length);


Des infos plus précises sont disponibles sur la même page que mmap()


Les 2 paramètres sont donc notre 'buffer' et la taille du fichier.
Code : C
1
munmap(buffer, (int)buf.st_size); // Desallocation


Cette ligne est très importante, car dans le cas de l'utilisation de la constante MAP_SHARED, l'appel à munmap() permet la sauvegarde des modifications!


Il ne vous reste plus qu'à compiler puis exécuter votre programme!
Code : Console
gcc memory_mapping.c -o ESSAI
./ESSAI
Chemin du fichier a mapper : test.txt
Contenu du fichier avant modification : ABCDEFGHIJ
Contenu du fichier apres modification : 0123456789


Pour les sceptiques, ouvrez manuellement votre fichier et vous constaterez que le fichier a bien été modifié. :)

Voici le code source complet du programme memory_mapping.c
Code : C
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>
 
typedef unsigned short ushort;
 
int main(int argc, char* const argv[])
{
        if (argc != 2)
        {
                printf("Usage : %s <fichier>\n", argv[0]);
                exit(EXIT_FAILURE);
        }
        
        int fd; // File descriptor
        ushort i; // Compteur
        char* buffer; // Contenu fichier en memoire
        struct stat buf; // Pour la taille du fichier
        
        if (-1 == (fd = open(argv[1], O_RDWR))) // Ouverture en lecture et ecriture
        {
                perror("open() ");
                exit(EXIT_FAILURE);
        }
        if (-1 == stat(argv[1], &buf)) // Obtention de la taille
        {
                perror("stat() ");
                close(fd); // Fermeture fichier!
                exit(EXIT_FAILURE);
        }
 
        buffer = (char*)mmap(0, (int)buf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); // Mappage
        if ((char*)-1 == buffer)
        {
                perror("mmap() ");
                close(fd); // fermeture fichier!
                exit(EXIT_FAILURE);
        }
        close(fd); // Fermeture du fichier
        printf("Contenu du fichier avant modification : %s\n", buffer);
 
        /* OPERATIONS A FAIRE ICI */
        for (i = 0 ; i < (int)buf.st_size ; i++)
                buffer[i] = i+'0'; // Remplacement (+'0' pour les chiffres ASCII)
 
        printf("Contenu du fichier apres modification : %s\n", buffer);
        munmap(buffer, (int)buf.st_size); // Desallocation & sauvegarde modifs
        
        return EXIT_SUCCESS;
}


Q.C.M.

La fonction open() retourne 0 si une erreur s'est produite ?
Pour mapper le fichier en mémoire ouvrir le fichier avec la constante O_RDONLY est suffisant ?
Une fois que j'ai réalisé les modifications que je voulais sur mon fichier, je peux sortir du programme directement.

Statistiques de réponses au QCM


Vous savez maintenant comment modifier un fichier autrement que par les fonctions de la bibliothèque standard fopen(), fread(), fwrite()

Pour des opérations sur des petits fichiers textes comme nous l'avons fait, le gain de performance est négligeable, mais sur des fichiers un peu plus gros il peut s'avérer utile.
Ce n'est pas une raison pour charger des très gros fichiers en mémoire hein :p
Retour en haut Retour en haut


Créé : le 10/12/2007 à 19:14:50
Modifié : le 22/08/2008 à 16:09:02
Avancement : 100%
Licence : Creative Commons BY-ND

L'orthographe, la grammaire et la présentation de ce tutoriel ont été vérifiées par les zCorrecteurs.

Changer de design | En savoir plus | Plan du site | Politique d'accessibilité | Règles | RSS tutoriels | RSS news
Édité par Simple IT SARL : Nous contacter | Notre blog | Revue de presse | Publicité

Y'a plus rien à lire, faut remonter maintenant !

Hébergement web - Correction de tutoriels - Créer un site
Vous souhaitez apparaître ici ? Contactez-nous.

Nombre de connectés 160 Zéros connectés | Requêtes SQL 8 requêtes | Temps de génération de la page : Total (SQL) 0.0666s (0.0563s)