Mmap() eine ganze große Datei

Lesezeit: 6 Minuten

Emers Benutzeravatar
Sem

Ich versuche, eine Binärdatei (~ 8 GB) mit dem folgenden Code (test.c) zu “mmappen”.

#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 wird kompiliert mit gcc -std=c99 test.c -o test und file der Testrücksendungen: test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

Obwohl dies für kleine Dateien gut funktioniert, erhalte ich einen Segmentierungsfehler, wenn ich versuche, eine große Datei zu laden. Das Programm gibt tatsächlich zurück:

Size: 8274324021 
mmap: Cannot allocate memory

Ich habe es geschafft, die gesamte Datei mit boost::iostreams::mapped_file zuzuordnen, aber ich möchte es mit C und Systemaufrufen tun. Was ist falsch an meinem Code?

  • Sie müssen die Datei mit dem öffnen O_LARGEFILE Flagge. Handbuch prüfen. unsicher ob >4GB Dateien sein können mmaped.

    – phoxie

    28. August 2011 um 16:35 Uhr

  • Kann hier nicht reproduziert werden. Ihr Code läuft gut auf einer 9G-Datei. Wie viel (RAM + SWAP) hast du? Was ist Ihre aktuelle /proc/sys/vm/overcommit_memory-Richtlinie?

    – Matte

    28. August 2011 um 16:42 Uhr

  • @Mat $free -m #Mem{ total: 1984, used: 1923, free 60} Swap{ total: 2021, used: 0, free: 2021} $ cat /proc/sys/vm/overcommit_memory #returns 0

    – Sem

    28. August 2011 um 16:52 Uhr


  • @phoxis Dieses Flag wäre von einem 32-Bit-Computer erforderlich. Ref

    – Sem

    28. August 2011 um 17:00 Uhr

  • @Emer: Entschuldigung, das ist mir gerade aufgefallen x86_64

    – phoxie

    28. August 2011 um 17:01 Uhr

Benutzeravatar von bdonlan
bdonlan

MAP_PRIVATE Mappings erfordern eine Speicherreservierung, da das Schreiben auf diese Seiten zu Copy-on-Write-Zuweisungen führen kann. Dies bedeutet, dass Sie nichts zu viel Größeres als Ihren physischen RAM + Swap abbilden können. Versuchen Sie es mit a MAP_SHARED stattdessen zuordnen. Das bedeutet, dass Schreibvorgänge in das Mapping auf der Festplatte widergespiegelt werden – daher weiß der Kernel, dass er durch Rückschreiben immer Speicher freigeben kann, sodass er Sie nicht einschränkt.

Ich stelle auch fest, dass Sie mit abbilden PROT_WRITE, aber Sie gehen dann weiter und lesen aus der Speicherzuordnung. Sie haben die Datei auch mit geöffnet O_RDONLY – dies selbst kann ein weiteres Problem für Sie sein; Sie müssen angeben O_RDWR wenn Sie verwenden möchten PROT_WRITE mit MAP_SHARED.

Wie für PROT_WRITE Nur funktioniert dies auf x86, da x86 keine schreibgeschützten Zuordnungen unterstützt, aber Segfaults auf anderen Plattformen verursachen kann. Anfrage PROT_READ|PROT_WRITE – oder, wenn Sie nur lesen müssen, PROT_READ.

Auf meinem System (VPS mit 676 MB RAM, 256 MB Swap) habe ich Ihr Problem reproduziert; wechseln zu MAP_SHARED ergibt ein EPERM Fehler (da ich nicht in die mit geöffnete Sicherungsdatei schreiben darf O_RDONLY). Wechseln zu PROT_READ und MAP_SHARED ermöglicht die erfolgreiche Zuordnung.

Wenn Sie Bytes in der Datei ändern müssen, besteht eine Möglichkeit darin, nur die Bereiche der Datei, in die Sie schreiben möchten, privat zu machen. Das ist, munmap und neu zuordnen mit MAP_PRIVATE die Bereiche, in die Sie schreiben möchten. Natürlich, wenn Sie beabsichtigen, an die zu schreiben gesamte Datei dann brauchst du dafür 8GB Speicher.

Alternativ können Sie auch schreiben 1 zu /proc/sys/vm/overcommit_memory. Dadurch kann die Zuordnungsanforderung erfolgreich ausgeführt werden. Denken Sie jedoch daran, dass Ihr Programm (oder ein anderes Programm!) vom OOM-Killer getötet wird, wenn Sie tatsächlich versuchen, die vollen 8 GB des COW-Speichers zu verwenden.

  • hmm .. Mat hat es geschafft, 8 * 3G (23G) mit 16G virtuellem Raum abzubilden. Es reserviert also nicht wirklich Speicher.

    – Karoly Horvath

    28. August 2011 um 17:18 Uhr


  • @yi_H, der Linux-Kernel erlaubt es Ihnen standardmäßig, physisch + Swap um einen bestimmten Prozentsatz zu überschreiten: opsmonkey.blogspot.com/2007/01/linux-memory-overcommit.html Abhängig von Ihrer Systemkonfiguration haben Sie jedoch möglicherweise eine strengere Konfiguration. Oder wenn Sie beispielsweise nur 4G insgesamt haben, können Sie das Limit überschreiten.

    – bdonlan

    28. August 2011 um 17:21 Uhr

  • Ich bin mir dessen bewusst, überprüfen Sie, was er geschrieben hat.

    – Karoly Horvath

    28. August 2011 um 17:24 Uhr

  • Ja, ich habe O_RDONLY und PROT_WRITE bemerkt. Ich muss jedoch Bytes der zugeordneten Datei austauschen, ohne sie zu ändern. Ich habe es mit PROT_READ und MAP_PRIVATE versucht und es hat nicht funktioniert. Danke für die Antwort

    – Sem

    28. August 2011 um 17:33 Uhr

  • @Emer, siehe meine Bearbeitung. Wenn Sie 8 GB Datei austauschen müssen, benötigen Sie 8 GB Arbeitsspeicher + Auslagerung, um das Ergebnis zu speichern.

    – bdonlan

    28. August 2011 um 17:36 Uhr

Linux (und anscheinend ein paar andere UNIX-Systeme) haben die MAP_NORESERVE Flagge für Karte(2), die verwendet werden kann, um die Überbelegung des Auslagerungsbereichs explizit zu aktivieren. Dies kann nützlich sein, wenn Sie eine Datei zuordnen möchten, die größer ist als der auf Ihrem System verfügbare freie Speicherplatz.

Dies ist besonders praktisch bei Verwendung mit MAP_PRIVATE und beabsichtigen nur, in einen kleinen Teil des speicherabgebildeten Bereichs zu schreiben, da dies andernfalls die Reservierung des Auslagerungsbereichs der gesamten Datei auslösen würde (oder das System zurückkehren würde ENOMEMwenn systemweites Overcommitting nicht aktiviert wurde und Sie den freien Speicher des Systems überschreiten).

Das Problem, auf das Sie achten sollten, ist, dass, wenn Sie in einen großen Teil dieses Speichers schreiben, die faule Swap-Speicherplatzreservierung dazu führen kann, dass Ihre Anwendung den gesamten freien RAM verbraucht und auf dem System auslagert, wodurch schließlich der OOM-Killer (Linux) oder ausgelöst wird wodurch Ihre App eine erhält SIGSEGV.

Sie haben nicht genügend virtuellen Speicher, um diese Zuordnung zu verarbeiten.

Als Beispiel habe ich hier eine Maschine mit 8 GB RAM und ~ 8 GB Swap (also insgesamt 16 GB verfügbarer virtueller Speicher).

Wenn ich Ihren Code auf einem VirtualBox-Snapshot mit ~ 8 GB ausführe, funktioniert er einwandfrei:

$ 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 

Wenn ich jetzt den Swap fallen lasse, bleiben mir 8 GB Gesamtspeicher. (Nicht Führen Sie dies auf einem aktiven Server aus.) Und das Ergebnis ist:

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

Stellen Sie also sicher, dass Sie über genügend virtuellen Speicher verfügen, um diese Zuordnung zu speichern (selbst wenn Sie nur ein paar Seiten in dieser Datei berühren).

  • Können Sie überprüfen, ob Sie zwei 8G-Dateien mit 8+8 virtuellem Speicherplatz abbilden können? Es klingt ein bisschen seltsam, dass es den Speicherplatz benötigt, obwohl einige Seiten möglicherweise nie geladen oder geändert werden (in diesem Fall können sie einfach gelöscht werden).

    – Karoly Horvath

    28. August 2011 um 17:01 Uhr


  • ich kann mmap Viele Dateien kleiner als 16 GB mit (8 GB + 8 GB) verfügbarem virtuellem Speicher (bis zu ~ 23 GB mit drei Dateien). Ich kann keine einzelne >8G-Datei nur mit 8G abbilden.

    – Matte

    28. August 2011 um 17:11 Uhr


  • Vielen Dank für den Test. Gibt es eine Möglichkeit, die genaue Menge an virtuellem Speicher zu berechnen, die zum Zuordnen einer Datei benötigt wird? oder es würde von vielen Faktoren abhängen?

    – Sem

    28. August 2011 um 17:36 Uhr

  • @Mat, 0 ist nicht ‘immer überschreiben’; 1 ist

    – bdonlan

    28. August 2011 um 17:52 Uhr

  • @bdonlan: du hast recht, tut mir leid – dieser ganze Kommentar war falsch

    – Matte

    28. August 2011 um 17:57 Uhr

1416580cookie-checkMmap() eine ganze große Datei

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy