Wann sollte ich mmap für den Dateizugriff verwenden?

276

POSIX-Umgebungen bieten mindestens zwei Möglichkeiten für den Zugriff auf Dateien. Es gibt die Standard - Systemaufrufe open(), read(), write(), und Freunde, aber es gibt auch die Möglichkeit der Verwendung mmap()der Datei in den virtuellen Speicher abzubilden.

Wann ist es vorzuziehen, eine über die andere zu verwenden? Was sind ihre individuellen Vorteile, die zwei Schnittstellen verdienen?

Peter Burns
quelle
16
Siehe auch mmap () vs. Leseblöcke und diesen Beitrag von Linus Torvalds, auf den in einer der Antworten dort verwiesen wird.
MvG

Antworten:

299

mmapist großartig, wenn mehrere Prozesse schreibgeschützt auf Daten aus derselben Datei zugreifen, was bei den von mir geschriebenen Serversystemen üblich ist. mmapErmöglicht all diesen Prozessen, dieselben physischen Speicherseiten gemeinsam zu nutzen, wodurch viel Speicherplatz gespart wird.

mmapAußerdem kann das Betriebssystem Paging-Vorgänge optimieren. Betrachten Sie beispielsweise zwei Programme. Programm, Adas eine 1MBDatei in einen Puffer einliest , der mit erstellt malloc, und Programm B, das mmapsdie 1-MB-Datei in den Speicher legt. Wenn das Betriebssystem einen Teil des ASpeichers austauschen muss, muss es den Inhalt des zu austauschenden Puffers schreiben, bevor es den Speicher wiederverwenden kann. In diesem BFall können unveränderte mmapSeiten sofort wiederverwendet werden, da das Betriebssystem weiß, wie sie aus der vorhandenen Datei wiederhergestellt werden können, aus der sie stammen mmap. (Das Betriebssystem kann erkennen, welche Seiten unverändert sind, indem es beschreibbare mmapSeiten zunächst als schreibgeschützt markiert und Seg-Fehler abfängt , ähnlich wie bei der Strategie "Beim Schreiben kopieren" .)

mmapist auch nützlich für die Kommunikation zwischen Prozessen . Sie können mmapeine Datei als Lese- / Schreibzugriff in den Prozessen verwenden, die kommunizieren müssen, und dann Synchronisationsprimitive in der mmap'dRegion verwenden (dafür ist das MAP_HASSEMAPHOREFlag vorgesehen).

Ein mmapunangenehmer Ort kann sein, wenn Sie mit sehr großen Dateien auf einem 32-Bit-Computer arbeiten müssen. Dies liegt daran mmap, dass im Adressraum Ihres Prozesses ein zusammenhängender Adressblock gefunden werden muss, der groß genug ist, um in den gesamten Bereich der zugeordneten Datei zu passen. Dies kann zu einem Problem werden, wenn Ihr Adressraum fragmentiert wird und möglicherweise 2 GB Adressraum frei sind, aber kein einzelner Bereich für eine Dateizuordnung von 1 GB geeignet ist. In diesem Fall müssen Sie die Datei möglicherweise in kleineren Blöcken zuordnen, als Sie möchten, dass sie passt.

Eine weitere mögliche Unannehmlichkeit mmapals Ersatz für Lesen / Schreiben besteht darin, dass Sie Ihre Zuordnung für Offsets der Seitengröße starten müssen. Wenn Sie nur einige Daten mit Offset Xabrufen möchten, müssen Sie diesen Offset korrigieren, damit er mit kompatibel ist mmap.

Und schließlich, Lesen / Schreiben der einzige Weg sind Sie können mit einigen Arten von Dateien. mmapkann nicht für Dinge wie Pipes und Ttys verwendet werden .

Don Neufeld
quelle
10
Können Sie mmap () für wachsende Dateien verwenden? Oder ist die Größe an dem Punkt festgelegt, an dem Sie den Speicher / die Datei mmap () zuweisen?
Jonathan Leffler
29
Wenn Sie den mmap-Aufruf durchführen, müssen Sie eine Größe angeben. Wenn Sie also so etwas wie eine Heckoperation durchführen möchten, ist dies nicht sehr geeignet.
Don Neufeld
5
Afaik MAP_HASSEMAPHOREist spezifisch für BSD.
Patrick Schlüter
6
@JonathanLeffler Natürlich können Sie mmap () für wachsende Dateien verwenden, aber Sie müssen mmap () erneut mit der neuen Größe aufrufen, wenn die Datei die Grenze des ursprünglich zugewiesenen Speicherplatzes erreicht. Das PosixMmapFile von LevelDB gibt Ihnen ein gutes Beispiel. Ab 1.15 wurde die Verwendung von mmap eingestellt. Sie können die alte Version von Github
Baotiao
4
mmap kann auch nützlich sein, wenn eine Datei in mehreren Durchgängen verarbeitet werden muss: Die Kosten für die Zuweisung virtueller Speicherseiten werden nur einmal bezahlt.
Fock
69

Ein Bereich, in dem ich fand, dass mmap () kein Vorteil ist, war das Lesen kleiner Dateien (unter 16 KB). Der Overhead von Seitenfehlern beim Lesen der gesamten Datei war sehr hoch im Vergleich zu nur einem einzigen read () - Systemaufruf. Dies liegt daran, dass der Kernel einen Lesevorgang in Ihrer Zeitscheibe manchmal vollständig zufriedenstellen kann, was bedeutet, dass Ihr Code nicht wegschaltet. Bei einem Seitenfehler schien es wahrscheinlicher, dass ein anderes Programm geplant wird, wodurch der Dateivorgang eine höhere Latenz aufweist.

Ben Combee
quelle
4
+1 Das kann ich bestätigen. Bei kleinen Dateien ist es schneller, mallocein Stück Speicher zu erstellen und 1 daraus readzu machen. Dies ermöglicht, dass derselbe Code, der Speicherzuordnungen verarbeitet, malloc'ed verarbeitet.
Patrick Schlüter
35
Dies sagte, Ihre Rechtfertigung dafür ist nicht richtig. Der Scheduler hat überhaupt nichts mit dem Unterschied zu tun. Der Unterschied ergibt sich aus den Schreibzugriffen auf die Seitentabellen. Hierbei handelt es sich um eine globale Struktur des Kernels, in der angegeben ist, welche Prozesse welche Speicherseite und ihre Zugriffsrechte enthalten. Dieser Vorgang kann sehr kostspielig sein (er kann Cache-Zeilen ungültig machen, er kann durch TLB gehen, die Tabelle ist global und muss vor gleichzeitigem Zugriff geschützt werden usw.). Sie benötigen eine bestimmte Kartengröße, damit der Overhead der readZugriffe höher ist als der Overhead der Manipulation des virtuellen Speichers.
Patrick Schlüter
1
@ PatrickSchlüter Okay, ich verstehe, dass zu Beginn von mmap () Overhead entsteht, bei dem die Seitentabelle geändert wird. Angenommen, wir ordnen 16 KB einer Datei dem Speicher zu. Bei einer Seitengröße von mmap4 KB müssen 4 Einträge in der Seitentabelle aktualisiert werden. Das readKopieren in einen Puffer von 16 KB umfasst jedoch auch das Aktualisieren von 4 Seitentabelleneinträgen, ganz zu schweigen davon, dass die 16 KB in den Benutzer-Adr-Bereich kopiert werden müssen. Könnten Sie also die Unterschiede der Vorgänge in der Seitentabelle erläutern und erläutern, wie teuer sie sind mmap?
Flow2k
45

mmaphat den Vorteil, wenn Sie wahlfreien Zugriff auf große Dateien haben. Ein weiterer Vorteil ist, dass Sie mit Speicheroperationen (memcpy, Zeigerarithmetik) darauf zugreifen können, ohne sich um die Pufferung zu kümmern. Normale E / A können bei Verwendung von Puffern manchmal recht schwierig sein, wenn Sie Strukturen haben, die größer als Ihr Puffer sind. Der zu handhabende Code ist oft schwer zu finden, mmap ist im Allgemeinen einfacher. Das heißt, es gibt bestimmte Fallen bei der Arbeit mit mmap. Wie bereits erwähnt, mmapist die Einrichtung recht kostspielig, daher lohnt es sich, sie nur für eine bestimmte Größe (von Maschine zu Maschine unterschiedlich) zu verwenden.

Für reine sequentielle Zugriffe auf die Datei ist dies auch nicht immer die bessere Lösung, obwohl ein entsprechender Aufruf madvisedas Problem mindern kann.

Sie müssen mit Ausrichtungsbeschränkungen Ihrer Architektur (SPARC, itanium) vorsichtig sein. Bei Lese- / Schreib-E / A sind die Puffer häufig richtig ausgerichtet und werden beim Dereferenzieren eines gegossenen Zeigers nicht abgefangen.

Sie müssen auch darauf achten, dass Sie nicht außerhalb der Karte zugreifen. Dies kann leicht passieren, wenn Sie Zeichenfolgenfunktionen auf Ihrer Karte verwenden und Ihre Datei am Ende keine \ 0 enthält. Dies funktioniert meistens, wenn Ihre Dateigröße nicht ein Vielfaches der Seitengröße beträgt, da die letzte Seite mit 0 gefüllt ist (der zugeordnete Bereich hat immer die Größe eines Vielfachen Ihrer Seitengröße).

Patrick Schlüter
quelle
30

Neben anderen netten Antworten ein Zitat aus der Linux-Systemprogrammierung von Googles Experte Robert Love:

Vorteile von mmap( )

Das Bearbeiten von Dateien über mmap( )hat eine Handvoll Vorteile gegenüber Standard- read( )und write( )Systemaufrufen. Unter ihnen sind:

  • Durch das Lesen und Schreiben in eine speicherabgebildete Datei wird die überflüssige Kopie vermieden, die bei Verwendung der read( )oder write( )Systemaufrufe auftritt , bei denen die Daten in einen und aus einem Benutzerbereichspuffer kopiert werden müssen.

  • Abgesehen von möglichen Seitenfehlern verursacht das Lesen und Schreiben in eine speicherabgebildete Datei keinen Systemaufruf oder Kontextwechselaufwand. Es ist so einfach wie der Zugriff auf den Speicher.

  • Wenn mehrere Prozesse dasselbe Objekt in den Speicher abbilden, werden die Daten von allen Prozessen gemeinsam genutzt. Schreibgeschützte und gemeinsam genutzte beschreibbare Zuordnungen werden in ihrer Gesamtheit gemeinsam genutzt. Bei privaten beschreibbaren Zuordnungen werden die noch nicht COW-Seiten (Copy-on-Write) gemeinsam genutzt.

  • Das Suchen in der Zuordnung erfordert triviale Zeigermanipulationen. Der lseek( )Systemaufruf ist nicht erforderlich .

Aus diesen Gründen mmap( )ist es eine kluge Wahl für viele Anwendungen.

Nachteile von mmap( )

Bei der Verwendung sind einige Punkte zu beachten mmap( ):

  • Speicherzuordnungen sind immer eine ganzzahlige Anzahl von Seiten. Somit wird der Unterschied zwischen der Größe der Sicherungsdatei und einer ganzzahligen Anzahl von Seiten als Leerraum "verschwendet". Bei kleinen Dateien kann ein erheblicher Prozentsatz der Zuordnung verschwendet werden. Bei 4-KB-Seiten verschwendet eine 7-Byte-Zuordnung beispielsweise 4.089 Byte.

  • Die Speicherzuordnungen müssen in den Adressraum des Prozesses passen. Bei einem 32-Bit-Adressraum kann eine sehr große Anzahl von Zuordnungen unterschiedlicher Größe zu einer Fragmentierung des Adressraums führen, wodurch es schwierig wird, große freie zusammenhängende Bereiche zu finden. Dieses Problem ist bei einem 64-Bit-Adressraum natürlich viel weniger offensichtlich.

  • Das Erstellen und Verwalten der Speicherzuordnungen und der zugehörigen Datenstrukturen im Kernel ist mit einem Mehraufwand verbunden. Dieser Overhead wird im Allgemeinen durch den Wegfall der im vorherigen Abschnitt erwähnten Doppelkopie vermieden, insbesondere bei größeren Dateien, auf die häufig zugegriffen wird.

Aus diesen Gründen werden die Vorteile von mmap( )am stärksten realisiert, wenn die zugeordnete Datei groß ist (und somit jeder verschwendete Speicherplatz einen kleinen Prozentsatz der gesamten Zuordnung ausmacht) oder wenn die Gesamtgröße der zugeordneten Datei gleichmäßig durch die Seitengröße teilbar ist ( und somit gibt es keinen verschwendeten Platz).

Miljen Mikic
quelle
13

Die Speicherzuordnung kann im Vergleich zu herkömmlichen E / A einen enormen Geschwindigkeitsvorteil bieten. Das Betriebssystem kann die Daten aus der Quelldatei lesen, wenn die Seiten in der Speicherzuordnungsdatei berührt werden. Dies funktioniert, indem fehlerhafte Seiten erstellt werden, die vom Betriebssystem erkannt werden, und dann lädt das Betriebssystem die entsprechenden Daten automatisch aus der Datei.

Dies funktioniert genauso wie der Paging-Mechanismus und wird normalerweise für Hochgeschwindigkeits-E / A optimiert, indem Daten zu Seitengrenzen und -größen des Systems (normalerweise 4 KB) gelesen werden - eine Größe, für die die meisten Dateisystem-Caches optimiert sind.

AndyG
quelle
15
Beachten Sie, dass mmap () nicht immer schneller als read () ist. Bei sequentiellen Lesevorgängen bietet mmap () keinen messbaren Vorteil - dies basiert auf empirischen und theoretischen Beweisen. Wenn Sie mir nicht glauben, schreiben Sie Ihren eigenen Test.
Tim Cooper
1
Ich kann Zahlen aus unserem Projekt angeben, eine Art Textindex für eine Phrasendatenbank. Der Index ist mehrere Gigabyte groß und die Schlüssel befinden sich in einem ternären Baum. Der Index wächst immer noch parallel zum Lesezugriff, der Zugriff außerhalb der zugeordneten Teile erfolgt über pread. Unter Solaris 9 Sparc (V890) ist der Zugriff auf das Pread zwei- bis dreimal langsamer als memcpyauf der MMap. Sie haben jedoch Recht, dass der sequentielle Zugriff nicht unbedingt schneller ist.
Patrick Schlüter
19
Nur ein kleiner Trottel. Es funktioniert nicht wie der Paging-Mechanismus, es ist der Paging-Mechanismus. Durch das Zuordnen einer Datei wird einer Datei anstelle der anonymen Auslagerungsdatei ein Speicherbereich zugewiesen.
Patrick Schlüter
2

Ein Vorteil, der noch nicht aufgeführt ist, ist die Möglichkeit mmap(), eine schreibgeschützte Zuordnung als saubere Seiten beizubehalten. Wenn man einen Puffer im Adressraum des Prozesses zuweist und dann read()den Puffer aus einer Datei füllt, sind die diesem Puffer entsprechenden Speicherseiten jetzt verschmutzt, seit sie geschrieben wurden.

Schmutzige Seiten können vom Kernel nicht aus dem RAM gelöscht werden. Wenn Swap Space vorhanden ist, können sie zum Swap ausgelagert werden. Dies ist jedoch kostspielig und auf einigen Systemen, z. B. kleinen eingebetteten Geräten mit nur Flash-Speicher, gibt es überhaupt keinen Austausch. In diesem Fall bleibt der Puffer im RAM stecken, bis der Prozess beendet wird, oder gibt ihn möglicherweise mit zurück madvise().

Nicht auf mmap()Seiten geschrieben sind sauber. Wenn der Kernel RAM benötigt, kann er diese einfach löschen und den RAM verwenden, in dem sich die Seiten befanden. Wenn der Prozess, bei dem das Mapping durchgeführt wurde, erneut darauf zugreift, führt dies zu einem Seitenfehler. Der Kernel lädt die Seiten aus der Datei neu, aus der sie ursprünglich stammen . Genauso wie sie überhaupt besiedelt waren.

Dies erfordert nicht mehr als einen Prozess, bei dem die zugeordnete Datei verwendet wird, um von Vorteil zu sein.

TrentP
quelle
Kann der Kernel eine "schmutzige" mmap-Seite nicht löschen, indem er ihren Inhalt zuerst in die zugrunde liegende Datei schreibt?
Jeremy Friesner
2
Bei der Verwendung read()haben die Seiten, auf denen Daten gespeichert werden, keine Beziehung zu der Datei, aus der sie möglicherweise stammen. Sie können also nur ausgeschrieben werden, um den Platz zu tauschen. Wenn eine Datei vorhanden ist mmap()edund die Zuordnung beschreibbar (im Gegensatz zu schreibgeschützt) und beschrieben ist, hängt dies davon ab, ob die Zuordnung war MAP_SHAREDoder war MAP_PRIVATE. Eine gemeinsam genutzte Zuordnung kann / muss in die Datei geschrieben werden, eine private jedoch nicht.
TrentP