Ein einfaches mmap() Readonly-Beispiel
Problem:
Du möchtest mmap() aus dem POSIX-Header sys/stat.h verwenden, um eine Datei für Lesezugriff (nicht Schreibzugriff) zu mappen. Du findest kein einfaches nacktes Beispiel im Internet.
Lösung
Wenn du nur an einem High-Level-Wrapper interessiert bist, schau dir dieses boost::iostreams mmap-Beispiel und die boost::iostreams mmap-Referenz an. Beachte jedoch, dass boost::iostreams nicht die gesamte Funktionalität von mmap() bietet und boost::iostreams eine ziemlich große Abhängigkeit ist (man muss die Bibliothek linken!). Es könnte jedoch die beste Option für bessere Portabilität auf Nicht-Unix-Systeme sein.
Hier ist eine minimale mmap()-basierte cat-Implementierung. Sie ist nicht effizient, demonstriert aber die wichtigsten Konzepte. Sie verwendet die stat()-basierte getFilesize()-Funktion aus diesem vorherigen Beitrag.
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <assert.h>
size_t getFilesize(const char* filename) {
struct stat st;
stat(filename, &st);
return st.st_size;
}
int main(int argc, char** argv) {
size_t filesize = getFilesize(argv[1]);
//Datei öffnen
int fd = open(argv[1], O_RDONLY, 0);
assert(fd != -1);
//mmap ausführen
void* mmappedData = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd, 0);
assert(mmappedData != MAP_FAILED);
//Die gemappten Daten auf stdout schreiben (= FD #1)
write(1, mmappedData, filesize);
//Aufräumen
int rc = munmap(mmappedData, filesize);
assert(rc == 0);
close(fd);
}Kompilieren mit
g++ -o mmap-example mmap-example.cppAusführen wie folgt:
echo "hello mmap world" > test.txt
./mmap-example test.txtSchritt-für-Schritt-Diskussion
Schritt 1: Dateigröße ermitteln. Diese musst du vor dem Aufruf von mmap kennen, aber du kannst auch nur eine Teilmenge mappen.
Schritt 2: Datei öffnen. Beachte, dass du die Funktion fopen() nicht direkt verwenden kannst (du könntest fileno() auf das FILE-Objekt anwenden), da du den Dateideskriptor brauchst! In diesem Fall verwenden wir O_RDONLY, weil wir mmap() nur-lesen verwenden wollen.
Schritt 3: mmap() mit diesen Parametern aufrufen:
- NULL: Lass den Kernel entscheiden, an welche Adresse gemappt wird. Dies ist der empfohlene Adressparameter.
- filesize: Die Größe der Datei. Für Readonly-mmap machen nur Werte <= der tatsächlichen Dateigröße Sinn.
PROT_READ: Datei nur-lesen mappen (mit anderen Worten, nur-Lese-Seitenschutz verwenden)MAP_PRIVATE: Normalerweise bedeutet dies Änderungen am gemappten Speicher nicht in die Datei zurückschreiben, aber wir haben sie sowieso nur-lesen gemappt, daher macht die Verwendung von MAP_SHARED keinen merklichen Unterschied.MAP_POPULATE: Lässt den Kernel Teile der Datei vorladen. Dies ist eine Performance-Optimierung. Es ist nicht zwingend erforderlich, dies zu verwenden.- fd: Der Unix-Dateideskriptor zum Mappen (wenn du ein
FILE*hast, kannst du die Deskriptornummer mitfileno()ermitteln). Muss geöffnet sein * 0: Der Offset vom Anfang der Datei. 0, weil wir die gesamte Datei mappen wollen.
Eine detaillierte mmap()-Referenz findest du auf der Opengroup-Seite zu mmap
Fehlerprüfung
Die obige Implementierung prüft nicht ordnungsgemäß auf Fehler, da es nur ein minimales Beispiel ist.
In jedem Fall solltest du zumindest prüfen, ob der von mmap() zurückgegebene Wert == MAP_FAILED ist. Siehe die Opengroup-Seite zu mmap für Details.
Update 2013/09/26: Dateideskriptor im Cleanup-Abschnitt schließen
Update 2016/01/07: != MAP_FAILED statt NULL asserten (Danke an Erik Sjölund)