mmap, munmap - (un)mapt Dateien oder Geräte im Speicher
Standard-C-Bibliothek (
libc,
-lc)
#include <sys/mman.h>
void *mmap(void Adr[.laenge], size_t laenge, int prot, int Schalter,
int dd, off_t Versatz);
int munmap(void Adr[.laenge], size_t laenge);
Siehe ANMERKUNGEN für Informationen über
Feature-Test-Makros-Anforderungen.
mmap() erstellt ein neues Mapping in den virtuellen Adressraum des
aufrufenden Prozesses. Die Anfangsadresse für dieses neue Mapping wird
in
Adr angegeben. Das Argument
laenge gibt an, welche
Größe das Mapping haben soll (dies muss größer als
0 sein).
Falls
Adr 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
Adr 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
laenge Byte
aus der Datei (oder einem anderen Objekt), die durch den Dateideskriptor
dd beschrieben wird, ab dem Versatz
Versatz verwendet werden.
Dies ist anders als beim anonymen Mapping, siehe
MAP_ANONYMOUS unten.
Versatz 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
dd 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
Schalter 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
Schalter
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 Schalter 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
Schalter 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 dd wird ignoriert, einige
Implementierungen verlangen aber, dass dd -1 ist, falls
MAP_ANONYMOUS (oder MAP_ANON) festgelegt ist, und portable
Anwendungen sollten dies sicherstellen. Das Argument Versatz sollte
0 sein. Die Unterstützung für MAP_ANONYMOUS mit
MAP_SHARED wurde in Linux 2.4 hinzugefügt.
- 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
-
Adr wird nicht als Hinweis interpretiert; legt das
Mapping genau an dieser Adresse an. Adr 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
Adr und laenge 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 Linuxversionen, 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 Adr
ä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.
- Beachten Sie, dass ältere Kernel, die den Schalter
MAP_FIXED_NOREPLACE nicht erkennen, typischerweise (bei der
Erkennung einer Kollision mit einem bereits bestehenden Mapping) auf den
Verhaltenstyp von »nicht- MAP_FIXED«
zurückfallen: sie liefern eine Adresse zurück, die sich von
der angeforderten Adresse unterscheidet. Daher sollte
rückwärtskompatible Software die zurückgelieferte
Adresse mit der angeforderten Adresse vergleichen.
- 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 Speicherseiten 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 standardmäßige große
Speicherseitengröße bereit; die Vorgabegröße
großer Speicherseiten 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 Speicherseiten 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). Vor Linux 2.6
hatte dieser Schalter nur für private, schreibbare Mappings eine
Wirkung.
-
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. Der Aufruf mmap()
schlägt nicht fehl, falls das Mapping nicht belegt werden kann
(beispielsweise aufgrund von Beschränkungen der Anzahl der
gemappten großen Seiten bei der Verwendung von MAP_HUGETLB).
Die Unterstützung für MAP_POPULATE zusammen mit
privaten Mappings wurde in Linux 2.6.23 hinzugefügt.
-
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).
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
Adr muss ein Vielfaches der Seitengröße sein
(für
laenge 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.
Bei Erfolg gibt
mmap einen Zeiger auf den gemappten Speicherbereich
zurück. Bei Fehlern wird
MAP_FAILED
(
(void *) -1) zurückgegeben und
errno
gesetzt, um den Fehler anzuzeigen.
Bei Erfolg liefert
munmap() 0 zurück. Im Fehlerfall liefert es -1
und
errno wird gesetzt, um den Fehler (wahrscheinlich
EINVAL)
anzuzeigen.
- EACCES
- Ein Dateideskriptor bezieht sich auf eine nicht normale
Datei. Oder ein Datei-Mapping wurde angefordert, aber dd ist nicht
zum Lesen geöffnet. Oder MAP_SHARED wurde erbeten und
PROT_WRITE ist gesetzt, aber dd 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
-
dd ist kein gültiger Dateideskriptor (und
MAP_ANONYMOUS wurde nicht gesetzt).
- EEXIST
-
MAP_FIXED_NOREPLACE wurde in Schalter
angegeben und der durch Adr und laenge abgedeckte Bereich
überschneidet sich mit einem bestehenden Mapping.
- EINVAL
- Die Adressen, die durch Adr, laenge oder
Versatz angegeben wurden, sind ungültig. (Z.B. sind sie zu
groß oder nicht an einer Speicherseitengröße
ausgerichtet.)
- EINVAL
- (seit Linux 2.6.12) laenge war 0.
- EINVAL
-
Schalter 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.
- ENOMEM
- Wir mögen Adr nicht, da es den virtuellen
Adressraum der CPU überschreitet.
- 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 laenge sowie die Anzahl
der für Versatz 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).
- EPERM
- Der Schalter MAP_HUGETLB wurde angegeben, aber der
Aufrufende war nicht privilegiert (verfügte nicht über die
Capability CAP_IPC_LOCK) und ist kein Mitglied der Gruppe
sysctl_hugetlb_shm_group; siehe die Beschreibung von
/proc/sys/vm/sysctl_hugetlb_shm_group in
- ETXTBSY
-
MAP_DENYWRITE wurde angegeben, aber das durch
dd 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.
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 |
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).)
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
Adr als
0 (NULL) und das Auslassen von
MAP_FIXED aus
Schalter. 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
Adr 0 (NULL) ist, dann wird die gemappte Adresse 0 (NULL) sein.
Bestimmte
Schalter-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.
Der einzige sichere Anwendungsfall für
MAP_FIXED ist, falls der
durch
Adr und
laenge 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.
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.
Für Mappings, die große Speicherseiten 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
Versatz ein Vielfaches der unterliegenden
Größe der großen Speicherseiten sein. Das System richtet
laenge automatisch aus, dass es ein Vielfaches der unterliegenden
Größe der großen Speicherseiten ist.
Für
munmap() müssen sowohl
Adr als auch
laenge ein Vielfaches der unterliegenden Größe der
großen Speicherseiten sein.
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 Linux 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
Versatz auf.
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.
Vor Linux 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
laenge 0
ist. Vor Linux 2.6.12 war
mmap() in diesem Fall allerdings erfolgreich:
es wurde kein Mapping erstellt und der Aufruf lieferte
Adr
zurück. Seit Linux 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).
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.
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
int
main(int argc, char *argv[])
{
int fd;
char *addr;
off_t offset, pa_offset;
size_t length;
ssize_t s;
struct stat sb;
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);
}
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.
Die deutsche Übersetzung dieser Handbuchseite wurde von Johnny
Teveßen <
[email protected]>, Martin Schulze
<
[email protected]>, Dr. Tobias Quathamer <
[email protected]> und
Helge Kreutzmann <
[email protected]> 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 die
Mailingliste
der Übersetzer