Scroll to navigation

MMAP(2) Linux-Programmierhandbuch MMAP(2)

BEZEICHNUNG

mmap, munmap - (un)mapt Dateien oder Geräte im Speicher

ÜBERSICHT

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,
           int fd, off_t offset);
int munmap(void *addr, size_t length);

Siehe ANMERKUNGEN für Informationen über Feature-Test-Makros-Anforderungen.

BESCHREIBUNG

mmap() erstellt ein neues Mapping in den virtuellen Adressraum des aufrufenden Prozesses. Die Anfangsadresse für dieses neue Mapping wird in addr angegeben. Das Argument length gibt an, welche Größe das Mapping haben soll (dies muss größer als 0 sein).

Falls addr NULL ist, wählt der Kernel die (Seiten-ausgerichtete) Adresse aus, an der das Mapping erstellt wird. Dies ist die portabelste Methode, ein neues Mapping zu erstellen. Falls addr nicht NULL ist, wertet der Kernel die Adresse als Hinweis, wo das Mapping erstellt werden soll. Unter Linux wird das Mapping dann eine Speicherseitengrenze in der Nähe auswählen (allerdings immer identisch zu oder oberhalb von dem durch /proc/sys/vm/mmap_min_addr festgelegten Wert) und versuchen, dort ein Mapping zu erstellen. Falls dort bereits ein anderes Mapping existiert, dann wählt der Kernel eine neue Adresse, die den Hinweis berücksichtigen kann, aber nicht muss. Die Adresse des neuen Mappings wird als Ergebnis des Aufrufs zurückgegeben.

Die Inhalte eines Datei-Mappings werden initialisiert, indem length Byte aus der Datei (oder einem anderen Objekt), die durch den Dateideskriptor fd beschrieben wird, ab dem Versatz offset verwendet werden. Dies ist anders als beim anonymen Mapping, siehe MAP_ANONYMOUS unten. offset muss ein Vielfaches der Seitengröße sein, die von sysconf(_SC_PAGE_SIZE) zurückgegeben wird.

Nachdem der mmap()-Aufruf zurückgekehrt ist, kann der Dateideskriptor fd sofort geschlossen werden, ohne dass das Mapping ungültig wird.

Das Argument prot beschreibt den gewünschten Speicherschutz des Mappings (und darf nicht im Widerspruch zum Öffnungsmodus der Datei stehen). Es ist entweder PROT_NONE oder das bitweise ODER von einem oder mehreren der folgenden Schalter:

PROT_EXEC
Seiten können ausgeführt werden.
PROT_READ
Seiten dürfen gelesen werden.
PROT_WRITE
Seiten dürfen beschrieben werden.
PROT_NONE
Auf die Seiten darf nicht zugegriffen werden.

Das Argument »flags«

Das Argument flags bestimmt, ob Aktualisierungen des Mappings für andere Prozesse sichtbar sind, die denselben Bereich mappen und ob Aktualisierungen auch in die zugrundeliegende Datei weitergereicht werden. Dieses Verhalten wird durch genau einen der folgenden Werte in flags festgelegt:
MAP_SHARED
Das Mapping gemeinsam benutzen. Aktualisierungen dieses Mappings sind für andere Prozesse in dem gleichen Bereich sichtbar und (falls es sich um Datei-basierende Mappings handelt) werden zu der zugrundeliegenden Datei weitergereicht. (Um genau zu steuern, wann Aktualisierungen zu der zugrundeliegenden Datei weitergereicht werden, muss msync(2) eingesetzt werden.)
MAP_SHARED_VALIDATE (seit Linux 4.15)
Dieser Schalter stellt das gleiche Verhalten wie MAP_SHARED bereit, außer dass MAP_SHARED-Mappings unbekannte Schalter in flags ignorieren. Im Gegensatz überprüft der Kernel, wenn er Mappings mittels MAP_SHARED_VALIDATE erstellt, dass alle übergebenen Schalter bekannt sind und schlägt mit dem Fehler EOPNOTSUPP bei unbekannten Schaltern fehl. Dieser Mapping-Typ wird auch benötigt, um bestimmte Mapping-Schalter (z.B. MAP_SYNC) verwenden zu können.
MAP_PRIVATE
Erstellt ein privates, beim Kopieren zu schreibendes Mapping. Aktualisierungen an dem Mapping sind für andere Prozesse, die die gleiche Datei mappen, nicht sichtbar, und werden nicht an die zugrundeliegende Datei weitergeleitet. Es ist nicht spezifiziert, ob Änderungen an der Datei, die nach dem Aufruf von mmap() erfolgen, in der gemappten Region sichtbar sind.

Sowohl MAP_SHARED als auch MAP_PRIVATE werden in POSIX.1-2001 und POSIX.1-2008 beschrieben. MAP_SHARED_VALIDATE ist eine Linux-Erweiterung.

Zusätzlich können null oder mehrere der folgenden Werte mit OR in flags hinzugefügt werden:

MAP_32BIT (seit Linux 2.4.20, 2.6)
Legt das Mapping in die ersten zwei Gigabyte des Prozessadressraums. Dieser Schalter wird nur auf X86-64 für 64-Bit-Programme unterstützt. Er wurde hinzugefügt, damit Thread-Stacks irgendwo innerhalb der ersten 2 GB Speicher zugewiesen werden können, damit die Leistung des Kontext-Umschaltens auf einigen der ersten 64-Bit-Prozessoren erhöht wird. Moderne X86-64-Prozessoren haben dieses Leistungsproblem nicht mehr, wodurch der Einsatz dieses Schalters auf diesen Systemen nicht mehr benötigt wird. Der Schalter MAP_32BIT wird ignoriert, wenn MAP_FIXED gesetzt ist.
MAP_ANON
Synonym für MAP_ANONYMOUS; zur Kompatibilität mit anderen Implementierungen bereitgestellt.
MAP_ANONYMOUS
Diesem Mapping liegt keine Datei zugrunde; ihr Inhalt wird mit Nullen initialisiert. Das Argument fd wird ignoriert, einige Implementierungen verlangen aber, dass fd -1 ist, falls MAP_ANONYMOUS (oder MAP_ANON) festgelegt ist, und portable Anwendungen sollten dies sicherstellen. Das Argument offset sollte 0 sein. Unter Linux wird die Kombination von MAP_ANONYMOUS mit MAP_SHARED erst ab Kernelversion 2.4 unterstützt.
MAP_DENYWRITE
Dieser Schalter wird ignoriert. (Vor langer Zeit — Linux 2.0 und älter — signalisierte er, dass Schreibversuche auf die zugrundeliegende Datei mit ETXTBSY fehlschlagen sollten. Dies war aber eine Quelle von Diensteverweigerungsangriffen.
MAP_EXECUTABLE
Dieser Schalter wird ignoriert.
MAP_FILE
Kompatibilitätsschalter. Ignoriert.
MAP_FIXED
addr wird nicht als Hinweis interpretiert; legt das Mapping genau an dieser Adresse an. addr muss geeignet ausgerichtet sein: bei den meisten Architekturen reicht ein Vielfaches der Seitengröße aus, allerdings könnten einige Architekturen zusätzliche Anforderungen stellen. Falls der mit addr und len festgelegte Speicherbereich bestehende Mappings überlappt, dann wird der überlappende Anteil des bestehenden Mappings verworfen. Falls die angegebene Adresse nicht verwandt werden kann, wird mmap() fehlschlagen.
Software, die Portierbarkeit anstrebt, sollte den Schalter MAP_FIXED mit Vorsicht verwenden und dabei berücksichtigen, dass sich die genaue Anordnung der Mappings des Prozesses im Speicher deutlich zwischen Kernelversionen, C-Bibliotheksversionen und Betriebssystemveröffentlichungen unterscheiden kann. Lesen Sie die Erörterung dieses Schalters im Abschnitt ANMERKUNGEN sorgfältig!
MAP_FIXED_NOREPLACE (seit Linux 4.17)
Dieser Schalter stellt ein Verhalten bereit, das MAP_FIXED im Hinblick auf die Erzwingung von addr ähnelt, sich aber dadurch unterscheidet, dass MAP_FIXED_NOREPLACE einen bereits bestehenden, gemappten Bereich durcheinanderbringt. Falls der angeforderte Bereich mit einem bestehenden Mapping kollidieren würde, dann schlägt dieser Aufruf mit EEXIST fehl. Dieser Schalter kann daher für atomare (im Hinblick auf andere Threads) Versuche, einen Adressbereich zu mappen, verwandt werden: ein Thread hat Erfolg, alle anderen berichten einen Fehlschlag.
Note that older kernels which do not recognize the MAP_FIXED_NOREPLACE flag will typically (upon detecting a collision with a preexisting mapping) fall back to a "non-MAP_FIXED" type of behavior: they will return an address that is different from the requested address. Therefore, backward-compatible software should check the returned address against the requested address.
MAP_GROWSDOWN
Dieser Schalter wird für Stacks verwandt. Er zeigt dem Kernelsystem für virtuellen Speicher an, dass sich das Mapping nach unten im Speicher ausdehnen soll. Die zurückgelieferte Adresse ist eine Seite tiefer als der Speicherbereich, der tatsächlich im virtuellen Adressraum des Prozesses erstellt wird. Wird eine Adresse in der »Wächter«-Seite unterhalb des Mappings berührt, dann wächst das Mapping um eine Seite. Dieses Wachstum kann wiederholt werden, bis das Mapping bis auf eine Seite innerhalb des hohen Endes des nächst-niedrigeren Mappings anwächst - zu diesem Zeitpunkt führt das Berühren der »Wächter«-Seite zu einem SIGSEGV-Signal.
MAP_HUGETLB (seit Linux 2.6.32)
Reserviert das Mapping mittels »großer Speicherseiten«. Siehe die Linux-Kernelquelldatei Documentation/admin-guide/mm/hugetlbpage.rst sowie die nachfolgenden ANMERKUNGEN für weitere Details.
MAP_HUGE_2MB, MAP_HUGE_1GB (seit Linux 3.8)
Wird in Zusammenhang mit MAP_HUGETLB verwandt, um alternative hugetlb-Seitengrößen (respektive 2 MB und 1 GB) auf Systemen auszuwählen, die mehrere hugetlb-Seitengrößen unterstützen.
Allgemeiner kann die gewünschte Größe der großen Seiten durch Kodierung des Logarithmus zur Basis 2 der gewünschten Seitengröße in den sechs Bits am Versatz MAP_HUGE_SHIFT konfiguriert werden. (Ein Wert 0 in diesem Bitfeld stellt die Vorgabe »große Seitengröße« bereit; die Vorgabe große Seitengröße kann mittels des durch /proc/meminfo offengelegten Feldes Hugepagesize ermittelt werden.) Daher sind die obigen zwei Konstanten wie folgt definiert:

#define MAP_HUGE_2MB    (21 << MAP_HUGE_SHIFT)
#define MAP_HUGE_1GB    (30 << MAP_HUGE_SHIFT)

    

Der von dem System unterstützte Bereich der Größe der großen Seiten kann durch Auflisten der Unterverzeichnisse in /sys/kernel/mm/hugepages ermittelt werden.
MAP_LOCKED (seit Linux 2.5.37)
Markiert den gemappten Bereich auf die gleiche Art wie mlock(2). Diese Implementierung wird versuchen, den gesamten Bereich vorzubelegen (»prefault«) aber der Aufruf von mmap() wird nicht mit ENOMEM fehlschlagen, falls dies nicht gelingt. Daher können später große Ausnahmebehandlungen passieren. Daher ist die Semantik nicht so stark wie mlock(2). Sie sollten mmap() mit mlock(2) verwenden, wenn große Ausnahmebehandlungen nach der Initialisierung des Mappings nicht akzeptierbar sind. Der Schalter MAP_LOCKED wird unter älteren Kerneln ignoriert.
MAP_NONBLOCK (seit Linux 2.5.46)
Dieser Schalter ergibt nur im Zusammenhang mit MAP_POPULATE Sinn. Es wird kein Vorauslesen durchgeführt, es werden Seitentabelleneinträge nur für Seiten erstellt, die bereits im RAM vorhanden sind. Seit Linux 2.6.23 führt dieser Schalter dazu, dass MAP_POPULATE nichts macht. Irgendwann könnte die Kombination von MAP_POPULATE und MAP_NONBLOCK wieder implementiert werden.
MAP_NORESERVE
Reserviert für dieses Mapping keinen Auslagerungsspeicher. Wenn Auslagerungsspeicher reserviert wird, muss garantiert werden, dass Änderungen an dem Mapping möglich sind. Wird kein Auslagerungsspeicher reserviert, könnte beim Schreiben ein SIGSEGV empfangen werden, falls kein physischer Speicher verfügbar ist. Siehe auch die Diskussion der Datei /proc/sys/vm/overcommit_memory in proc(5). In Kerneln vor 2.6 hatte dieser Schalter nur eine Wirkung für private, schreibbare Mappings.
MAP_POPULATE (seit Linux 2.5.46)
Belegt (»prefault«) Seitentabellen für ein Mapping. Für ein Datei-Mapping führt dies zu einem Vorablesen der Datei. Dies hilft dabei, später Blockierungen bei Seitenausnahmebehandlungen zu vermeiden. MAP_POPULATE wird für private Mappings erst seit Linux 2.6.23 unterstützt.
MAP_STACK (seit Linux 2.6.27)
Reserverviert ein Mapping an einer Adresse, die für einen Stack eines Prozesses oder Threads geeignet ist.
Dieser Schalter löst derzeit nichts aus. Durch Einsatz dieses Schalters können Anwendungen aber transparent sicherstellen, dass sie die Unterstützung erlangen, wenn dieser Schalter in der Zukunft implementiert wird. Daher wird er in der Glibc-Threading-Implementierung verwandt, um der Tatsache Rechnung zu tragen, dass auf einigen Architekturen (später) eine besondere Behandlung von Stack-Zuweisungen nötig sein könnte. Ein weiterer Grund, diesen Schalter einzusetzen, ist die Portabilität: MAP_STACK existiert (und hat eine Auswirkung) auf einigen anderen Systemen (z.B. einigen BSDs).
MAP_SYNC (seit Linux 4.15)
Dieser Schalter ist nur mit dem Mapping-Typ MAP_SHARED_VALIDATE verfügbar. Mappings vom Typ MAP_SHARED ignorieren diesen Schalter ohne Rückmeldung. Dieser Schalter wird nur für Dateien, die DAX (direktes Mapping von dauerhaftem Speicher) unterstützen, unterstützt. Für andere Dateien wird das Erstellen eines Mappings mit diesem Schalter zu einem EOPNOTSUPP-Fehler führen.
Gemeinsame Datei-Mappings mit diesem Schalter garantieren, dass der schreibbare eingemappte Speicheranteil im Adressraum des Prozesses auch in der gleichen Datei an dem gleichen Versatz selbst nach einem Systemabsturz oder -neustart sichtbar ist. Im Zusammenhang mit dem Einsatz geeigneter CPU-Anweisungen stellt dieses Benutzern solcher Mappings eine effizientere Art bereit, solche Datenveränderungen dauerhaft zu machen.
MAP_UNINITIALIZED (seit Linux 2.6.33)
Die anonymen Seiten nicht bereinigen. Dieser Schalter ist für die Verbesserung der Leistung auf eingebetteten Systemen gedacht. Dieser Schalter wird nur berücksichtigt, falls der Kernel mit der Option CONFIG_MMAP_ALLOW_UNINITIALIZED konfiguriert worden war. Aufgrund der Sicherheitsauswirkungen wird diese Option normalerweise nur auf eingebetteten Geräten (d.h. Geräten, bei denen komplette Kontrolle über die Speicherinhalte besteht) aktiviert.

Von den obigen Schaltern ist nur MAP_FIXED in POSIX.1-2001 und POSIX.1-2008 spezifiziert. Allerdings unterstützen die meisten Systeme MAP_ANONYMOUS (oder sein Synonym MAP_ANON).

munmap()

Der munmap-Systemaufruf hebt die Mappings im angegebenen Speicherbereich auf. Zukünftige Zugriffe auf diesen Adressraum erzeugen dann einen Fehler vom Typ »invalid memory reference« - Ungültiger Speicherzugriff. Der Adressraum wird außerdem automatisch ausgemappt, wenn der Prozess beendet wird. Das Schließen des Dateideskriptors hingegen führt nicht dazu, dass der Adress-Mapping aufgehoben wird.

Die Adresse addr muss ein Vielfaches der Seitengröße sein (für length ist das nicht der Fall). Alle Seiten, die einen Teil des angezeigten Bereichs enthalten, werden ausgemappt, und nachfolgende Referenzen auf diese Seiten führen zu SIGSEGV. Es ist kein Fehler, falls der angezeigte Bereich keine gemappten Seiten enthält.

RÜCKGABEWERT

Bei Erfolg gibt mmap einen Zeiger auf den gemappten Speicherbereich zurück. Bei Fehlern wird MAP_FAILED ((void *) -1) zurückgegeben und errno entsprechend gesetzt, um die Fehlerursache anzuzeigen.

Bei Erfolg liefert munmap() 0 zurück. Im Fehlerfall liefert es -1 und errno wird auf den Grund des Fehlers gesetzt (wahrscheinlich EINVAL).

FEHLER

EACCES
Ein Dateideskriptor bezieht sich auf eine nicht normale Datei. Oder ein Datei-Mapping wurde angefordert, aber fd ist nicht zum Lesen geöffnet. Oder MAP_SHARED wurde erbeten und PROT_WRITE ist gesetzt, aber fd ist nicht zum Lesen/Schreiben geöffnet (O_RDWR). Oder PROT_WRITE ist angegeben, aber die Datei darf nur am Ende weiter beschrieben werden (»append-only«).
EAGAIN
Die Datei wurde gesperrt oder zuviel Speicher wurde gesperrt (siehe setrlimit(2)).
EBADF
fd ist kein gültiger Dateideskriptor (und MAP_ANONYMOUS wurde nicht gesetzt).
EEXIST
MAP_FIXED_NOREPLACE wurde in flags angegeben und der durch addr und length abgedeckte Bereich überschneidet sich mit einem bestehenden Mapping.
EINVAL
Die Adressen, die durch addr, length oder offset angegeben wurden, sind ungültig. (Z.B. sind sie zu groß oder nicht an einer Speicherseitengröße ausgerichtet.)
EINVAL
(seit Linux 2.6.12) length war 0.
EINVAL
flags enthielt weder MAP_PRIVATE, MAP_SHARED noch MAP_SHARED_VALIDATE.
ENFILE
Die systemweite Beschränkung für die Gesamtzahl offener Dateien wurde erreicht.
ENODEV
Das zugrundeliegende Dateisystem der angegebenen Datei unterstützt das Speicher-Mapping nicht.
ENOMEM
Es ist kein Speicher verfügbar.
ENOMEM
Die maximale Anzahl von Mappings des Prozesses würde überschritten. Dieser Fehler kann auch für munmap() beim Aufheben von Mappings einer Region in der Mitte eines bestehenden Mappings auftreten, da dies zu zwei kleineren Mappings auf jeder Seite des entmappten Bereichs führt.
ENOMEM
(seit Linux 4.7). Die Prozessbeschränkung RLIMIT_DATA, beschrieben in getrlimit(2), würde überschritten.
EOVERFLOW
Auf 32-Bit-Architekturen zusammen mit den Erweiterungen für große Dateien (d.h. der Verwendung von 64-Bit off_t): die Anzahl der für length sowie die Anzahl der für offset verwandten Seiten würde einen Überlauf von unsigned long (32-Bit) hervorrufen.
EPERM
Das Argument prot verlangt PROT_EXEC, aber der gemappte Bereich gehört zu einer Datei auf einem Dateisystem, das ohne Ausführrechte eingehängt wurde (»no-exec«).
EPERM
Die Aktion wurde durch eine Dateiversiegelung verhindert; siehe fcntl(2).
ETXTBSY
MAP_DENYWRITE wurde angegeben, aber das durch fd bezeichnete Objekt ist zum Schreiben geöffnet.

Die Verwendung eines gemappten Bereichs kann diese Signale verursachen:

SIGSEGV
Es wurde versucht, in einen Bereich zu schreiben, der nur lesbar gemappt wurde.
SIGBUS
Es wurde versucht, auf eine Seite des Puffers zuzugreifen, die hinter dem Ende der gemappten Datei liegt. Für eine Erläuterung der Behandlung von Bytes in der Seite, die dem Ende der gemappten Datei entspricht, die kein Vielfaches der Seitengröße ist, siehe ANMERKUNGEN.

ATTRIBUTE

Siehe attributes(7) für eine Erläuterung der in diesem Abschnitt verwandten Ausdrücke.
Schnittstelle Attribut Wert
mmap(), munmap() Multithread-Fähigkeit MT-Safe

KONFORM ZU

POSIX.1-2001, POSIX.1-2008, SVr4, 4.4BSD.

Auf POSIX-Systemen, auf denen mmap(), msync(2) und munmap() verfügbar sind, ist _POSIX_MAPPED_FILES in <unistd.h> auf einen Wert größer 0 definiert. (Siehe auch sysconf(3).)

ANMERKUNGEN

Speicher, der mit mmap() gemappt wurde, wird über fork(2) hinweg mit den gleichen Attributen erhalten.

Eine Datei wird in Vielfachen der Seitengröße gemappt. Für eine Datei, die nicht ein Vielfaches der Seitengröße ist, werden die verbliebenen Bytes in der unvollständigen Seite am Ende des Mappings beim Mappen mit Nullen überschrieben und Änderungen an diesem Bereich werden nicht in die Datei geschrieben. Es ist nicht spezifiziert, wie sich die Größenänderung der zugrundeliegenden Datei auf das Mapping der Seiten, die hinzugefügten oder entfernten Regionen der Datei entsprechen, auswirkt.

Auf einigen Hardware-Architekturen (z.B. i386) impliziert PROT_WRITE PROT_READ. Es ist architekturabhängig, ob PROT_READ PROT_EXEC impliziert (oder nicht). Portable Programme sollten immer PROT_EXEC setzen, falls sie vorhaben, Code in dem neuen Mapping auszuführen.

Die portierbare Art, ein Mapping zu erstellen, ist die Angabe von addr als 0 (NULL) und das Auslassen von MAP_FIXED aus flags. In diesem Fall wählt das System die Adresse für das Mapping; die Adresse wird so gewählt, dass sie mit keinem bestehenden Mapping in Konflikt steht und nicht 0 sein wird. Falls der Schalter MAP_FIXED angegeben und addr 0 (NULL) ist, dann wird die gemappte Adresse 0 (NULL) sein.

Bestimmte flags-Konstanten sind nur definiert, falls die geeigneten Feature-Test-Makros definiert sind (möglicherweise standardmäßig): _DEFAULT_SOURCE mit Glibc 2.19 oder neuer; oder _BSD_SOURCE oder _SVID_SOURCE in Glibc 2.19 und älter. (Es reicht auch aus, _GNU_SOURCE einzusetzen, und dieses Makro zu verlangen, wäre logischer gewesen, da alle diese Schalter Linux-spezifisch sind). Die relevanten Schalter sind: MAP_32BIT, MAP_ANONYMOUS (und das Synonym MAP_ANON), MAP_DENYWRITE, MAP_EXECUTABLE, MAP_FILE, MAP_GROWSDOWN, MAP_HUGETLB, MAP_LOCKED, MAP_NONBLOCK, MAP_NORESERVE, MAP_POPULATE und MAP_STACK.

Durch Verwendung von mincore(2) kann eine Anwendung ermitteln, welche Seiten eines Mappings sich derzeit im Puffer/Seitenzwischenspeicher befinden.

MAP_FIXED sicher benutzen

Der einzige sichere Anwendungsfall für MAP_FIXED ist, falls der durch addr und length festgelegte Adressbereich vorher durch ein anderes Mapping reserviert wurde; andernfalls ist die Verwendung von MAP_FIXED gefährlich, da sie bereits bestehende Mappings zwangsweise entfernt, wodurch es für einen Prozess mit mehreren Threads leicht wird, seinen eigenen Adressraum zu beschädigen.

Nehmen wir beispielsweise an, dass Thread A /proc/<PID>/maps durchsucht, um einen nicht benutzten Adressbereich zu finden, den er mittels MAP_FIXED mappen kann, während Thread B gleichzeitig Teile des gleichen Adressbereichs (oder den gesamten Adressbereich) erlangt. Wenn Thread A anschließend mmap(MAP_FIXED) einsetzt, wird es das Mapping, das Thread B erstellte, durcheinanderbringen. In diesem Szenario muss Thread B nicht das Mapping direkt erstellen: einfach ein Aufruf einer Bibliotheksfunktion, die intern dlopen(3) zum Laden einer anderen dynamische Bibliothek verwendet, reicht aus. Der Aufruf von dlopen(3) wird die Bibliothek in den Adressraum des Prozesses einmappen. Desweiteren kann fast jeder Bibliotheksaufruf auf eine Art implementiert sein, die Speicher-Mappings zu dem Adressraum hinzufügt, entweder mit dieser Technik oder einfach durch Reservierung von Speicher. Beispiele sind brk(2), malloc(3), pthread_create(3) und die PAM-Bibliotheken http://www.linux-pam.org.

Seit Linux 4.17 kann ein Multithread-Programm den Schalter MAP_FIXED_NOREPLACE verwenden, um die oben beschriebene Gefahr zu vermeiden, dass ein Mapping an einer festen Adresse versucht wird, die nicht durch ein bereits existierendes Mapping reserviert wurde.

Zeitstempeländerungen für Datei-basierte Mappings

Für Datei-basierte Mappings wird das Feld st_atime für die gemappte Datei zu jedem Zeitpunkt zwischen mmap() und dem entsprechenden Entmappen aufgerufen werden; die erste Referenz auf die gemappte Seite wird das Feld aktualisieren, falls es nicht bereits erfolgt ist.

Das Feld st_ctime und st_mtime für eine mit PROT_WRITE und MAP_SHARED gemappte Datei wird nach einem Schreibzugriff auf den gemappten Bereich und vor dem nachfolgenden msync(2) mit den Schalter MS_SYNC oder MS_ASYNC, falls dieser erfolgt, aktualisiert.

Mappings großer Seiten (Huge TLB)

Für Mappings, die große Seiten einsetzen, unterscheiden sich die Anforderungen für die Argumente von mmap() und munmap() etwas von den Anforderungen für Mappings, die die native Systemseitengröße verwenden.

Für mmap() muss offset ein Vielfaches der unterliegenden Größe der großen Seiten sein. Das System richtet length automatisch aus, dass es ein Vielfaches der unterliegenden Größe der großen Seiten ist.

Für munmap() müssen sowohl addr als auch length ein Vielfaches der unterliegenden Größe der großen Seiten sein.

Unterschiede C-Bibliothek/Kernel

Diese Seite beschreibt die durch den mmap()-Wrapper der Glibc bereitgestellte Funktion. Ursprünglich rief diese Funktion einen Systemaufruf mit dem gleichen Namen auf. Seit Kernel 2.4 wurde dieser Systemaufruf durch mmap2(2) ersetzt und heutzutage ruft die Wrapperfunktion mmap() der Glibc mmap2(2) mit einem geeignet angepassten Wert für offset auf.

FEHLER

Unter Linux gibt es keine Garantien, wie die, die unter MAP_NORESERVE vorgeschlagen werden. Standardmäßig kann jeder Prozess jederzeit getötet werden, wenn dem System der Speicher ausgeht.

In Kerneln vor 2.6.7 hatte der Schalter MAP_POPULATE nur einen Effekt, falls prot als PROT_NONE festgelegt ist.

SUSv3 spezifiziert, dass mmap() fehlschlagen soll, falls length 0 ist. In Kerneln vor 2.6.12 war mmap() in diesem Fall allerdings erfolgreich: es wurde kein Mapping erstellt und der Aufruf lieferte addr zurück. Seit Kernel 2.6.12 schlägt es in diesem Fall mit dem Fehler EINVAL fehl.

POSIX spezifiziert, dass das System immer jede teilweise gefüllte Seite am Ende des Objektes mit Nullen auffüllen muss und dass das System niemals Änderungen an dem Objekt hinter seinem Ende schreibt. Unter Linux verbleiben sämtliche geschriebenen Daten in solchen Teilseiten nach dem Ende des Objektes im Seitenzwischenspeicher, selbst nachdem die Datei geschlossen und entmappt wurde und selbst obwohl die Daten niemals zu der Datei selbst geschrieben wurden, könnten nachfolgende Mappings die veränderten Inhalte sehen. In einigen Fällen könnte dies durch einen Aufruf von msync(2), bevor das Aufheben des Mappings stattfindet, behoben werden, allerdings funktioniert dies nicht auf tmpfs(5) (beispielsweise beim Einsatz der POSIX-Schnittstelle für gemeinsamen Speicher, wie in shm_overview(7) dokumentiert).

BEISPIELE

Das nachfolgende Programm gibt Teile der als sein erstes Befehlszeilenargument übergebenen Datei auf die Standardausgabe aus. Der ausgegebene Byte-Bereich wird mittels des Versatzes und des Längenwertes im zweiten und dritten Befehlszeilenargument angegeben. Das Programm erstellt ein Speicher-Mapping der benötigten Seiten der Datei und verwendet write(2), um die gewünschten Bytes auszugeben.

Programmquelltext

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)
int
main(int argc, char *argv[])
{
    char *addr;
    int fd;
    struct stat sb;
    off_t offset, pa_offset;
    size_t length;
    ssize_t s;
    if (argc < 3 || argc > 4) {
        fprintf(stderr, "%s Dateiversatz [Länge]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    fd = open(argv[1], O_RDONLY);
    if (fd == -1)
        handle_error("open");
    if (fstat(fd, &sb) == -1)           /* Um die Dateigröße zu erhalten */
        handle_error("fstat");
    offset = atoi(argv[2]);
    pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
        /* Versatz für mmap() muss an der Seite ausgerichtet sein */
    if (offset >= sb.st_size) {
        fprintf(stderr, "Versatz ist hinter dem Dateiende\n");
        exit(EXIT_FAILURE);
    }
    if (argc == 4) {
        length = atoi(argv[3]);
        if (offset + length > sb.st_size)
            length = sb.st_size - offset;
                /* Bytes hinter dem Dateiende können nicht angezeigt werden */
    } else {    /* Kein Längen-Argument ==> Anzeige bis zum Dateiende */
        length = sb.st_size - offset;
    }
    addr = mmap(NULL, length + offset - pa_offset, PROT_READ,
                MAP_PRIVATE, fd, pa_offset);
    if (addr == MAP_FAILED)
        handle_error("mmap");
    s = write(STDOUT_FILENO, addr + offset - pa_offset, length);
    if (s != length) {
        if (s == -1)
            handle_error("write");
        fprintf(stderr, "Schreiben unvollständig");
        exit(EXIT_FAILURE);
    }
    munmap(addr, length + offset - pa_offset);
    close(fd);
    exit(EXIT_SUCCESS);
}

SIEHE AUCH

ftruncate(2), getpagesize(2), memfd_create(2), mincore(2), mlock(2), mmap2(2), mprotect(2), mremap(2), msync(2), remap_file_pages(2), setrlimit(2), shmat(2), userfaultfd(2), shm_open(3), shm_overview(7)

Die Beschreibung der folgenden Dateien in proc(5): /proc/[PID]/maps, /proc/[PID]/map_files und /proc/[pid]/smaps.

B.O. Gallmeister, POSIX.4, O'Reilly, Seiten 128–129 und 389–391.

KOLOPHON

Diese Seite ist Teil der Veröffentlichung 5.10 des Projekts Linux-man-pages. Eine Beschreibung des Projekts, Informationen, wie Fehler gemeldet werden können sowie die aktuelle Version dieser Seite finden sich unter https://www.kernel.org/doc/man-pages/.

ÜBERSETZUNG

Die deutsche Übersetzung dieser Handbuchseite wurde von Johnny Teveßen <j.tevessen@gmx.de>, Martin Schulze <joey@infodrom.org>, Dr. Tobias Quathamer <toddy@debian.org> und Helge Kreutzmann <debian@helgefjell.de> erstellt.

Diese Übersetzung ist Freie Dokumentation; lesen Sie die GNU General Public License Version 3 oder neuer bezüglich der Copyright-Bedingungen. Es wird KEINE HAFTUNG übernommen.

Wenn Sie Fehler in der Übersetzung dieser Handbuchseite finden, schicken Sie bitte eine E-Mail an <debian-l10n-german@lists.debian.org>.

21. Dezember 2020 Linux