Es wird gesagt, dass mmap()
Dateien in den Speicher abbildet und dabei den virtuellen Adressraum des aufrufenden Prozesses benötigt. Kopiert es tatsächlich Daten in den Speicher oder existieren die Daten weiterhin auf der Festplatte? Ist mmap()
schneller als read()
?
Antworten
Zu viele Anzeigen?Das einzige, was die Funktion mmap
wirklich tut, ist einige Kernel-Datenstrukturen zu ändern und möglicherweise die Seitentabelle. Tatsächlich wird dabei überhaupt nichts in den physischen Speicher geschrieben. Nachdem Sie mmap
aufgerufen haben, zeigt der allokierte Bereich wahrscheinlich nicht einmal auf den physischen Speicher: Wenn darauf zugegriffen wird, wird ein Seitenfehler verursacht. Dieser Art von Seitenfehler wird vom Kernel transparent behandelt, tatsächlich ist dies eine der Hauptaufgaben des Kernels.
Bei mmap
bleibt die Daten auf der Festplatte und wird von dort in den Speicher kopiert, wenn Ihr Prozess darauf zugreift. Es kann auch spekulativ in den physischen Speicher kopiert werden. Wenn Ihr Prozess ausgelagert wird, müssen die Seiten im mmap
-Bereich nicht in den Swap geschrieben werden, da sie bereits von einem langfristigen Speicher unterstützt werden - es sei denn, Sie haben sie natürlich geändert.
Allerdings belegt mmap
virtuellen Adressraum, genau wie malloc
und andere ähnliche Funktionen (die hauptsächlich mmap
im Hintergrund verwenden, oder sbrk
, was im Grunde eine spezielle Version von mmap
ist). Der Hauptunterschied zwischen der Verwendung von mmap
zum Lesen einer Datei und read
zum Lesen einer Datei ist, dass unveränderte Seiten in einem mmap
-Bereich nicht zum allgemeinen Speicherdruck beitragen, sie sind fast "kostenlos", speichermäßig gesehen, solange sie nicht verwendet werden. Im Gegensatz dazu tragen Dateien, die mit der Funktion read
gelesen werden, immer zum Speicherdruck bei, unabhängig davon, ob sie verwendet oder geändert wurden.
Zu guter Letzt ist mmap
gegenüber read
nur in den begünstigten Anwendungsfällen - zufälliger Zugriff und Seitennutzung - schneller. Für das lineare Durchlaufen einer Datei, insbesondere einer kleinen Datei, wird read
in der Regel schneller sein, da es keine Änderungen an den Seitentabellen erfordert und weniger Systemaufrufe benötigt.
Als Empfehlung kann ich sagen, dass jede große Datei, die Sie durchsuchen werden, auf 64-Bit-Systemen im Allgemeinen mit mmap
und auf 32-Bit-Systemen in Teilen gemappt werden sollte, da der virtuelle Speicher dort weniger verfügbar ist.
Siehe auch: mmap() vs. reading blocks
Siehe auch (Dank an James): When should I use mmap for file access?
Die Daten sind immer noch auf der Festplatte vorhanden. Das Betriebssystem allokiert etwas physischen Speicher und kopiert die Dateidaten dorthin, damit Sie auf den Dateiinhalt zugreifen können (das geschieht, wenn Sie versuchen, auf Dateidaten zuzugreifen). Dieser physische Speicher wird in den virtuellen Adressraum des Prozesses abgebildet. Das Betriebssystem kann lange ungenutzte Teile der Datei abmappen und sie bei Bedarf wieder abmappen. Wenn nur wenig freier physischer Speicher vorhanden ist, können Abmappungen aggressiver sein und zu einer schlechten Leistung führen.
Speicherabbildung von Dateien:
-
verwendet weniger physischen Speicher und virtuellen Adressraum als einfache "Dateilese-/schreib"-Vorgänge, weil es hier und da keine Dateipuffer gibt (im Betriebssystem, in der C-Standardbibliothek und in Ihrem Programm) und kein unnötiges Kopieren zwischen ihnen.
-
kann (
und wahrscheinlich ist es, wenn Sie genügend freien physischen Speicher habenunter bestimmten Bedingungen, je nachdem, wie viel Daten wir sprechen und wie viel physischen Speicher das Betriebssystem uns für mmap'ing zur Verfügung stellt) schneller sein als einfache "Dateilese-/schreib"-Vorgänge aufgrund des oben Genannten und weil Sie Übergänge zwischen den Benutzer- und Kernel-Modi vermeiden, die der "Dateilese"-Systemaufruf mit sich bringt. Die einzigen verbleibenden Übergänge sind diejenigen, um eine bestimmte Seite abzubilden, die derzeit nicht abgebildet ist. Diese werden durch Seitenausnahmen verursacht (=CPU-Ausnahmen), die im Kernel behandelt werden. Solange alles, was Sie benötigen, abgebildet ist, gibt es keine Benutzer-Kernel-Übergänge beim Zugriff auf Dateidaten.
"Virtueller Speicher" eines Prozesses ist der Bereich der für ihn verfügbaren Adressen. Um etwas im Speicher verfügbar zu machen, müssen Sie einen Adressbereich reservieren, daher belegt mmap()
einen Teil des virtuellen Speichers.
Unter Linux (und wahrscheinlich verwenden viele andere Systeme einen ähnlichen Mechanismus) wird beim Lesen einer Datei der Inhalt zunächst in vom Kernel allokierten Speicher (bei Linux ist dies der "Seitencache") geladen. Verwenden Sie dann mmap()
, wird dieser Speicher dem Prozess einfach durch Zuweisen einer Adresse im Adressraum dieses Prozesses zur Verfügung gestellt. Verwenden Sie read()
, allokiert der Prozess einen Puffer, der sowohl Adressen (virtueller Speicher) als auch einen Ort zum Leben (physischer Speicher) benötigt, und die Daten werden aus dem Seitencache in diesen Puffer kopiert (mehr physischer Speicher ist erforderlich).
Die Daten werden nur von der Festplatte gelesen, wenn sie tatsächlich zugegriffen werden. Im Fall von mmap()
bedeutet dies, wenn Sie tatsächlich auf den Speicher zugreifen, bei read()
erfolgt dies durch das Kopieren in Ihren Puffer, also innerhalb des read()
-Aufrufs.
Daher ist mmap()
effizienter für große Dateien, insbesondere für zufälligen Zugriff. Die Nachteile sind, dass es nur für Dateien und nicht dateiähnliche Objekte (Pipes, Sockets, Geräte, /proc-Dateien usw.) verwendet werden kann und dass ein E/A-Fehler beim Seitenfehler erkannt wird, der schwer zu handhaben ist (er sendet ein SIGBUS-Signal), während read einen Fehler zurückgeben kann und die Anwendung versuchen kann, sich zu erholen (die meisten tun es sowieso nicht). Letzteres betrifft hauptsächlich Netzdateisysteme, bei denen ein E/A-Fehler aufgrund einer verlorenen Verbindung auftreten kann.