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?
- vous devez ouvrir le fichier avec le
O_LARGEFILE
drapeau. vérifier le manuel. pas sûr que >fichiers de 4 go peut êtremmap
ed. - Ne pouvons pas reproduire ici. Votre code fonctionne très bien sur un 9G fichier. Combien (RAM+SWAP) avez-vous? Quelle est votre /proc/sys/vm/overcommit_memory politique?
- total: 1984, utilisé: 1923, gratuit de 60} Swap{ total: 2021, utilisé: 0, gratuit: 2021} $ cat /proc/sys/vm/overcommit_memory #retourne 0
- le drapeau serait nécessaire à partir d'un ordinateur 32 bits. Ref
- désolé, je viens de remarquer
x86_64
- On dirait que vous avez eu la réponse à votre problème particulier, mais cette erreur peut aussi être causée par avoir
ulimit -v
trop bas pour la quantité de mémoire que vous demandez, quelle que soit la quantité de mémoire/swap que vous avez. - Avez-vous essayez d'exécuter
strace
pour voir si vous êtes en trébuchant sur un ENOMEM?
Vous devez vous connecter pour publier un commentaire.
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 unMAP_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 aussi 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 avecO_RDONLY
- ce qui, en soi, peut être un autre problème pour vous; vous devez spécifierO_RDWR
si vous souhaitez utiliserPROT_WRITE
avecMAP_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. DemandePROT_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; changer à
MAP_SHARED
résultats dans unEPERM
erreur (puisque je ne suis pas autorisé à écrire dans le fichier de sauvegarde est ouverte avecO_RDONLY
). Changer dePROT_READ
etMAP_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 avecMAP_PRIVATE
les domaines où vous avez l'intention d'écrire à. Bien sûr, si vous avez l'intention d'écrire à la fichier entier alors vous avez besoin de 8 go de mémoire pour le faire.Alternativement, vous pouvez écrire
1
à/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.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:
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:
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).
mmap
plusieurs fichiers plus petits que 16G avec (8G+8G) de la mémoire virtuelle disponible (je suis allé jusqu'à ~ 23G avec trois fichiers). Je ne peux pas la carte d'un seul >8G fichier avec juste le 8G.Linux (et apparemment quelques autres systèmes UNIX) ont la
MAP_NORESERVE
drapeau pour 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 retourENOMEM
, 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
.