72 votes

Mmap() l'ensemble d'un fichier de grande taille

Je suis en train de "mmap" un fichier binaire (~ 8 go) en utilisant le code suivant (test.c).

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define handle_error(msg) \
  do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char *argv[])
{
   const char *memblock;
   int fd;
   struct stat sb;

   fd = open(argv[1], O_RDONLY);
   fstat(fd, &sb);
   printf("Size: %lu\n", (uint64_t)sb.st_size);

   memblock = mmap(NULL, sb.st_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
   if (memblock == MAP_FAILED) handle_error("mmap");

   for(uint64_t i = 0; i < 10; i++)
   {
     printf("[%lu]=%X ", i, memblock[i]);
   }
   printf("\n");
   return 0;
}

test.c est compilé à l'aide de gcc -std=c99 test.c -o test et file de test renvoie: test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

Bien que cela fonctionne bien pour les petits fichiers, j'obtiens une erreur de segmentation quand j'essaye de charger un gros. Le programme retourne en fait:

Size: 8274324021 
mmap: Cannot allocate memory

J'ai réussi à cartographier l'ensemble du dossier à l'aide de boost::iostreams::mapped_file mais je veux le faire à l'aide de C et des appels système. Quel est le problème avec mon code?

80voto

bdonlan Points 90068

MAP_PRIVATE mappages nécessitent une mémoire de réserve, comme l'écriture de ces pages peut entraîner de copie sur écriture allocations. Cela signifie que vous ne pouvez pas mapper quelque chose de beaucoup plus grand que votre physique de ram + swap. Essayez d'utiliser un MAP_SHARED cartographie de la place. Cela signifie que écrit à la cartographie pourra être répercuté sur le disque en tant que tel, le noyau sait qu'il peut toujours libérer de la mémoire en faisant de l'écriture différée, afin de ne pas vous limiter.

Je note également que vous mettez en correspondance avec PROT_WRITE, mais vous devez aller sur et lire à partir de la mémoire. Vous aussi ouvert le fichier avec l' O_RDONLY - ce qui, en soi, peut être un autre problème pour vous; vous devez spécifier O_RDWR si vous souhaitez utiliser PROT_WRITE avec MAP_SHARED.

Comme pour PROT_WRITE seulement, ce qui se passe à travailler sur des systèmes x86, car x86 ne supporte pas l'écriture seule mappages, mais peut entraîner des erreurs de segmentation sur d'autres plates-formes. Demande PROT_READ|PROT_WRITE - ou, si vous avez seulement besoin de lire, PROT_READ.

Sur mon système (VPS avec 676MB de RAM, 256 mo de swap), j'ai reproduit votre problème; le changement d' MAP_SHARED résultats en EPERM d'erreur (puisque je ne suis pas autorisé à écrire dans le fichier de sauvegarde est ouverte avec O_RDONLY). Changer d' PROT_READ et MAP_SHARED permet la cartographie de réussir.

Si vous avez besoin de modifier des octets dans le fichier, une option serait de faire en privé juste les plages du fichier que vous allez écrire. C'est, munmap et reconfigurer avec MAP_PRIVATE les domaines que vous voulez écrire. Bien sûr, si vous avez l'intention d'écrire à l' ensemble du fichier , puis vous avez besoin de 8 go de mémoire pour le faire.

Alternativement, vous pouvez écrire 1 de /proc/sys/vm/overcommit_memory. Cela permettra à la cartographie de demande pour réussir; cependant, gardez à l'esprit que si vous avez réellement essayez d'utiliser la totalité de 8 go de VACHE de la mémoire, votre programme (ou un autre programme!) seront tués par l'OOM killer.

5voto

dcoles Points 548

Linux (et apparemment quelques autres systèmes UNIX) ont l' MAP_NORESERVE drapeau de mmap(2), qui peut être utilisé pour activer explicitement l'espace de swap overcommitting. Cela peut être utile lorsque vous souhaitez mapper un fichier de plus de la quantité de mémoire disponible sur votre système.

C'est particulièrement pratique lorsqu'il est utilisé avec MAP_PRIVATE et seulement l'intention d'écrire une petite partie de la mémoire mappée gamme, car cela entraînerait par ailleurs de l'espace de swap de réservation de l'ensemble du dossier (ou le système de retour d' ENOMEM, si à l'échelle du système overcommitting n'a pas été activée et que vous avez dépassé la libérer de la mémoire du système).

La question à surveiller est que si vous écrivez une grande partie de ce mémoire, le paresseux de l'espace de swap de réservation peut causer à votre application de consommer toute la mémoire RAM libre et d'échange sur le système, éventuellement le déclenchement de l'OOM killer (Linux) ou la cause de votre app pour recevoir un SIGSEGV.

4voto

Mat Points 104488

Vous n'avez pas assez de mémoire virtuelle pour traiter la cartographie.

Comme un exemple, j'ai une machine ici avec 8G de RAM, et ~8G swap (donc 16G de mémoire virtuelle totale disponible).

Si j'exécute votre code sur une VirtualBox instantané qui est ~8G, il fonctionne très bien:

$ ls -lh /media/vms/.../snap.vdi
-rw------- 1 me users 9.2G Aug  6 16:02 /media/vms/.../snap.vdi
$ ./a.out /media/vms/.../snap.vdi
Size: 9820000256 
[0]=3C [1]=3C [2]=3C [3]=20 [4]=4F [5]=72 [6]=61 [7]=63 [8]=6C [9]=65 

Maintenant, si je baisse le swap, je suis à gauche avec 8G de mémoire totale. (Ne pas l'exécuter sur un serveur actif.) Et le résultat est:

$ sudo swapoff -a
$ ./a.out /media/vms/.../snap.vdi
Size: 9820000256 
mmap: Cannot allocate memory

Donc, assurez-vous que vous avez assez de mémoire virtuelle à tenir que la cartographie (même si vous ne touchez à quelques pages de ce fichier).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X