Quand dois-je utiliser mmap pour accéder aux fichiers?

Les environnements POSIX offrent au moins deux manières d’accéder aux fichiers. Il y a les appels système standards open() , read() , write() et friends, mais il y a aussi la possibilité d’utiliser mmap() pour mapper le fichier en mémoire virtuelle.

Quand est-il préférable d’utiliser l’un sur l’autre? Quels sont leurs avantages individuels, dont deux interfaces?

mmap est génial si vous avez plusieurs processus accédant aux données en lecture seule à partir du même fichier, ce qui est courant dans les systèmes de serveur que j’écris. mmap permet à tous ces processus de partager les mêmes pages de mémoire physique, économisant ainsi beaucoup de mémoire.

mmap permet également au système d’exploitation d’optimiser les opérations de pagination. Par exemple, considérons deux programmes; programme A qui lit dans un fichier de 1 Mo dans un tampon de création avec malloc et programme B qui mappe le fichier de 1 Mo en mémoire. Si le système d’exploitation doit échanger une partie de la mémoire de A, il doit écrire le contenu du tampon pour échanger avant de pouvoir réutiliser la mémoire. Dans le cas de B, toutes les pages mmap’d non modifiées peuvent être réutilisées immédiatement car le système d’exploitation sait comment les restaurer à partir du fichier existant à partir duquel elles ont été enregistrées. (Le système d’exploitation peut détecter les pages qui n’ont pas été modifiées en marquant d’abord les pages en écriture inscriptibles en lecture seule et en détectant les erreurs seg, semblables à la stratégie de copie sur écriture).

mmap est également utile pour la communication entre processus. Vous pouvez mapper un fichier en lecture / écriture dans les processus devant communiquer, puis utiliser des primitives de synchronisation dans la région mmap’d (c’est ce à quoi sert l’indicateur MAP_HASSEMAPHORE).

Un seul endroit où mmap peut être difficile est de travailler avec des fichiers très volumineux sur une machine 32 bits. Cela est dû au fait que mmap doit trouver un bloc d’adresses contigu dans l’espace d’adressage de votre processus suffisamment grand pour s’adapter à toute la plage du fichier en cours de mappage. Cela peut devenir un problème si votre espace d’adressage devient fragmenté, où vous pouvez disposer de 2 Go d’espace d’adressage, mais aucune plage individuelle ne peut correspondre à un mappage de fichiers de 1 Go. Dans ce cas, vous devrez peut-être mapper le fichier en morceaux plus petits que vous ne le souhaitez.

Un autre inconvénient potentiel de mmap en remplacement de read / write est que vous devez commencer votre mappage sur les décalages de la taille de la page. Si vous voulez simplement obtenir des données à l’offset X, vous devrez corriger ce décalage pour qu’il soit compatible avec mmap.

Et enfin, la lecture / écriture est la seule façon de travailler avec certains types de fichiers. mmap ne peut pas être utilisé sur des choses comme les tuyaux et les ttys.

Lorsque j’ai lu des petits fichiers (moins de 16 Ko), j’ai trouvé que mmap () n’était pas un avantage. La surcharge de la page pour lire le fichier entier était très élevée par rapport à un simple appel système read (). Cela est dû au fait que le kernel peut parfois saturer complètement une lecture dans votre tranche de temps, ce qui signifie que votre code ne s’écarte pas. Avec un défaut de page, il semblait plus probable qu’un autre programme soit programmé, ce qui rendrait l’opération du fichier plus longue.

mmap a l’avantage lorsque vous avez un access aléatoire aux gros fichiers. Un autre avantage est que vous y accédez avec des opérations de mémoire (memcpy, arithmétique de pointeur), sans vous soucier de la mise en mémoire tampon. Les E / S normales peuvent parfois être assez difficiles lorsque vous utilisez des tampons lorsque vos structures sont plus grandes que votre tampon. Le code à traiter qui est souvent difficile à comprendre, mmap est généralement plus facile. Cela dit, il y a certains pièges lorsque vous travaillez avec mmap . Comme nous l’avons déjà mentionné, la configuration de mmap est très coûteuse, il convient donc de ne l’utiliser que pour une taille donnée (variable d’une machine à l’autre).

Pour les access séquentiels purs au fichier, ce n’est pas toujours la meilleure solution, bien qu’un appel approprié à madvise puisse atténuer le problème.

Vous devez faire attention aux ressortingctions d’alignement de votre architecture (SPARC, itanium), avec les E / S en lecture / écriture, les tampons sont souvent correctement alignés et ne bloquent pas le déréférencement d’un pointeur converti.

Vous devez également faire attention à ne pas accéder en dehors de la carte. Cela peut facilement se produire si vous utilisez des fonctions de chaîne sur votre carte et que votre fichier ne contient pas de \ 0 à la fin. Il fonctionnera la plupart du temps lorsque la taille de votre fichier n’est pas un multiple de la taille de la page car la dernière page est remplie avec 0 (la zone mappée est toujours de la taille d’un multiple de la taille de votre page).

En plus d’autres bonnes réponses, une citation de la programmation Linux écrite par l’expert de Google, Robert Love:

Avantages de mmap( )

La manipulation de fichiers via mmap( ) présente quelques avantages par rapport aux appels système standard read( ) et write( ) . Parmi eux:

  • La lecture et l’écriture dans un fichier mappé en mémoire évitent la copie superflue qui se produit lors de l’utilisation des appels système read( ) ou write( ) , où les données doivent être copiées vers et depuis un tampon d’espace utilisateur.

  • Mis à part d’éventuelles erreurs de page, la lecture et l’écriture dans un fichier mappé en mémoire n’entraînent aucun appel système ou changement de contexte. C’est aussi simple que d’accéder à la mémoire.

  • Lorsque plusieurs processus mappent le même object en mémoire, les données sont partagées entre tous les processus. Les mappages accessibles en lecture seule et partagés sont partagés dans leur intégralité; les mappages inscriptibles privés ont leurs pages pas encore COW (copie sur écriture) partagées.

  • Faire le tour du mapping implique des manipulations de pointeur sortingviales. L’appel système lseek( ) n’est pas nécessaire.

Pour ces raisons, mmap( ) est un choix judicieux pour de nombreuses applications.

Inconvénients de mmap( )

Il y a quelques points à garder à l’esprit lorsque vous utilisez mmap( ) :

  • Les mappages de mémoire sont toujours un nombre entier de pages en taille. Ainsi, la différence entre la taille du fichier de sauvegarde et un nombre entier de pages est “gaspillée” en tant qu’espace libre. Pour les petits fichiers, un pourcentage important du mappage peut être gaspillé. Par exemple, avec des pages de 4 Ko, un mappage de 7 octets gaspille 4 089 octets.

  • Les mappages de mémoire doivent tenir dans l’espace d’adressage du processus. Avec un espace d’adressage de 32 bits, un très grand nombre de correspondances de différentes tailles peut entraîner une fragmentation de l’espace d’adressage, ce qui rend difficile la recherche de grandes régions contiguës libres. Bien entendu, ce problème est beaucoup moins évident avec un espace d’adressage de 64 bits.

  • La création et la maintenance des mappages de mémoire et des structures de données associées au sein du kernel entraînent une surcharge. Cette surcharge est généralement éliminée par l’élimination de la double copie mentionnée dans la section précédente, en particulier pour les fichiers les plus importants et les plus fréquemment utilisés.

Pour ces raisons, les avantages de mmap( ) sont plus importants lorsque le fichier mappé est volumineux (et donc tout espace perdu est un petit pourcentage du mappage total), ou lorsque la taille totale du fichier mappé est divisible par le fichier. taille de la page (et donc il n’y a pas d’espace perdu).

La cartographie de la mémoire présente un potentiel de gain de vitesse considérable par rapport aux IO traditionnelles. Il permet au système d’exploitation de lire les données du fichier source lorsque les pages du fichier mappé en mémoire sont touchées. Cela fonctionne en créant des pages défectueuses, que le système d’exploitation détecte, puis le système d’exploitation charge automatiquement les données correspondantes à partir du fichier.

Cela fonctionne de la même manière que le mécanisme de pagination et est généralement optimisé pour les E / S haut débit en lisant les données sur les limites et les tailles des pages système (généralement 4K).