mremap(2)

BEZEICHNUNG
mremap - eine virtuelle Speicheradresse neu mappen

BIBLIOTHEK
Standard-C-Bibliothek (libc, -lc)

ÜBERSICHT
#define _GNU_SOURCE         /* siehe feature_test_macros(7) */
#include <sys/mman.h>

void *mremap(size_t alte_Größe;
             void alte_Adresse[alte_Größe], size_t alte_Größe,
             size_t neue_Größe, int Schalter, …  /* void *neue_Adresse */);

BESCHREIBUNG
mremap() erweitert (oder verkleinert) ein bestehendes Speicher-Mapping, potenziell durch gleichzeitiges Verschieben (bestimmt durch das Argument Schalter und den zur Verfügung stehenden virtuellen Speicherplatz).

Mappings könne auch einfach (ohne Größenänderung) durch Angabe identischer alte_Größe und neue_Größe und dem Schalter MREMAP_FIXED verschoben werden (Details weiter unten). Seit Linux 6.17 kann alte_Größe über mehrere Mappings einschließlich nicht gemappter Bereiche zwischen ihnen gehen, wenn eine einfache Verschiebung erfolgt; allerdings muss alte_Adresse gemappt sein. Der Schalter MREMAP_DONTUNMAP kann auch festgelegt werden.

Falls die Operation eine Verkleinerung ist, das bedeutet, alte_Größe größer als neue_Größe ist, dann kann alte_Größe über mehrere Mappings gehen, die nicht aneinander anschließen müssen. Falls diese Verkleinerung direkt vor Ort erfolgt, das bedeutet, weder MREMAP_FIXED noch MREMAP_DONTUNMAP angegeben sind, dann kann auch neue_Größe über mehrere VMAs gehen. Falls der Bereich allerdings verschoben wird, dann darf neue_Größe nur über ein einzelnes Mapping gehen.

Falls die Operation weder eine MREMAP_FIXED-Verschiebung noch -Verkleinerung ist, dann darf alte_Größe nur über ein einzelnes Mapping gehen.

alte_Adresse ist die alte Adresse des ersten virtuellen Speicherblocks, der vergrößert, verkleinert und/oder verschoben werden soll. Beachten Sie, dass alte_Adresse an den Speicherseiten ausgerichtet sein muss. alte_Größe ist die Größe des Bereichs, der die zu verändernden virtuellen Speicherblöcke enthält. neue_Größe ist die angeforderte Größe der virtuellen Speicherblöcke nach der Größenänderung. Optional kann ein fünftes Argument, neue_Adresse, angegeben werden; siehe die folgende Beschreibung von MREMAP_FIXED.

Falls der Wert von alte_Größe Null ist und sich alte_Adresse auf ein gemeinsam benutzbares Mapping bezieht (siehe die Beschreibung von MAP_SHARED in mmap(2)), dann wird mremap() ein neues Mapping der gleichen Seiten erstellen. neue_Größe wird die Größe des neuen Mappings sein und der Ort des neuen Mappings kann mit neue_Adresse festgelegt werden, siehe die nachfolgende Beschreibung von MREMAP_FIXED. Falls mittels dieser Methode ein neues Mapping angefordert wird, dann muss der Schalter MREMAP_MAYMOVE angegeben werden.

Das Bitmasken-Argument Schalter kann 0 sein oder die folgenden Schalter enthalten:

MREMAP_MAYMOVE
    Per Voreinstellung schlägt mremap() fehl, wenn an der aktuellen Position nicht ausreichend Platz vorhanden ist, um ein Mapping zu vergrößern. Wird dieser Schalter angegeben, darf der Kernel das Speicher-Mapping an eine neue virtuelle Adresse verlegen, falls das erforderlich ist. Wenn das Mapping verlegt wurde, werden absolute Zeiger zum Ort des alten Mappings ungültig. (Es sollten Versätze relativ zur Anfangsadresse des Mappings verwendet werden.)

MREMAP_FIXED (seit Linux 2.3.31)
    Dieser Schalter dient einem ähnlichen Zweck wie der Schalter MAP_FIXED von mmap(2). Wenn dieser Schalter angegeben wird, dann akzeptiert mremap() ein fünftes Argument, void *neue_Adresse, das eine an Seiten ausgerichtete Adresse angibt, an die das Mapping verschoben werden muss. Alle früheren Mappings auf den von neue_Adresse und neue_Größe angegebenen Adressbereich werden verworfen.

    Falls MREMAP_FIXED angegeben wird, muss ebenfalls MREMAP_MAYMOVE angegeben werden.

    Seit Linux 6.17 darf alte_Größe über das Mapping, in dem sich alte_Adresse befindet, hinausgehen, falls alte_Größe identisch zu neue_Größe ist und MREMAP_FIXED angegeben wurde. In diesem Fall werden Löcher zwischen den Mappings in dem ursprünglichen Bereich in dem neuen Bereich beibehalten. Die gesamte Operation wird atomar durchgeführt, außer wenn ein Fehler auftritt. Dann kann die Operation teilweise abgeschlossen werden, das bedeutet, einige Mappings können verschoben werden, andere nicht.

    Das Verschieben mehrerer Mappings ist nicht erlaubt, falls einer dieser Mappings mit userfaultfd(2) oder Mapping-Treibern registriert wurde, die ihre eigene, angepasste Adress-Mapping-Logik festlegen.

MREMAP_DONTUNMAP (seit Linux 5.7)
    Dieser Schalter, der zusammen mit MREMAP_MAYMOVE verwandt werden muss, verlegt Mappings auf eine neue Adresse, aber entfernt sie nicht von ihrer ursprünglichen Adresse.

    Der Schalter MREMAP_DONTUNMAP kann nur zusammen mit Mappings verwandt werden, die nicht VM_DONTEXPAND oder VM_PFNMAP sind. Vor Linux 5.13 konnte der Schalter MREMAP_DONTUNMAP nur mit privaten anonymen Mappings verwandt werden (siehe die Beschreibung von MAP_PRIVATE und MAP_ANONYMOUS in mmap(2)).

    Nach Abschluss wird jeder Zugriff auf den durch alte_Adresse und alte_Größe festgelegten Bereich zu einer Seitenausnahmebehandlung führen. Die Seitenausnahmebehandlung wird durch den Handler userfaultfd(2) erledigt, falls die Adresse in dem vorher mit userfaultfd(2) registrierten Bereich liegt. Andernfalls wird der Kernel einen mit Nullen gefüllten Bereich belegen, der die Ausnahmebehandlung erledigt.

    Der Schalter MREMAP_DONTUNMAP kann zur atomaren Verschiebung eines Mappings verwandt werden, wobei die Quelle gemappt bleibt. Siehe ANMERKUNGEN für mögliche Anwendungen von MREMAP_DONTUNMAP.

Falls die durch alte_Adresse und alte_Größe angegebenen Speichersegmente gesperrt sind (mittels mlock(2) oder etwas Ähnlichem), wird diese Sperre aufrecht erhalten, wenn die Speichersegmente verschoben oder ihre Größe geändert werden. Als Folge davon kann sich die Größe des durch einen Prozess gesperrten Speichers ändern.

RÜCKGABEWERT
Bei Erfolg gibt mremap() einen Zeiger auf den neuen virtuellen Speicherbereich zurück. Im Fehlerfall wird der Wert von MAP_FAILED (d.h. (void *) -1) zurückgegeben und errno gesetzt, um den Fehler anzuzeigen.

FEHLER

EAGAIN
    Der Aufrufende versuchte, ein gesperrtes Speichersegment zu vergrößern. Das war nicht möglich, ohne die Resourcen-Begrenzung RLIMIT_MEMLOCK zu überschreiten.

EFAULT
    Eine Adresse im Bereich von alte_Adresse bis alte_Adresse+alte_Größe ist für diesen Prozess eine ungültige virtuelle Speicheradresse. Man erhält sogar dann EFAULT, wenn Mappings existieren, die den gesamten angeforderten Adressraum abdecken, aber von unterschiedlichem Typ sind und die auszuführende mremap()-Operation unterstützt dies nicht.

EINVAL
    Ein ungültiges Argument wurde übergeben. Mögliche Ursachen sind:

    •   alte_Adresse war nicht an der Seitengrenze ausgerichtet

    •   ein von MREMAP_MAYMOVE, MREMAP_FIXED oder MREMAP_DONTUNMAP verschiedener Wert wurde in Schalter übergeben

    •   neue_Größe war Null

    •   neue_Größe oder neue_Adresse war ungültig

    •   der neue Adressbereich, der in neue_Adresse und neue_Größe angegeben wurde, überlappte den in alte_Adresse und alte_Größe angegebenen alten Adressbereich

    •   MREMAP_FIXED oder MREMAP_DONTUNMAP wurde angegeben, ohne auch MREMAP_MAYMOVE anzugeben

    •   MREMAP_DONTUNMAP wurde angegeben, aber eine oder mehrere Seiten in dem durch alte_Adresse und alte_Größe festgelegten Bereich waren nicht privat anonym

    •   MREMAP_DONTUNMAP wurde angegeben und alte_Größe war nicht zu neue_Größe identisch

    •   alte_Größe war Null und alte_Adresse bezieht sich nicht auf ein gemeinsam benutzbares Mapping (siehe aber FEHLER)

    •   alte_Größe war Null und der Schalter MREMAP_MAYMOVE war nicht angegeben

ENOMEM
    Zur Ausführung der Aktion war nicht genug Speicher verfügbar. Mögliche Ursachen sind:

    •   Der Speicherbereich kann an der aktuellen virtuellen Adresse nicht erweitert werden und in Schalter ist der Schalter MREMAP_MAYMOVE nicht gesetzt. Oder es gibt nicht genug freien (virtuellen) Speicher.

    •   MREMAP_DONTUNMAP wurde verwandt und verursachte dadurch die Erstellung eines neuen Mappings, das den verfügbaren (virtuellen) Speicher überschreiten würde. Oder es würde die maximale Anzahl an erlaubten Mappings überschreiten.

STANDARDS
Linux.

GESCHICHTE
Vor Version 2.4 machte die Glibc die Definition von MREMAP_FIXED nicht verfügbar und der Prototyp für mremap() ließ das Argument neue_Adresse nicht zu.

ANMERKUNGEN
mremap() ändert das Mapping zwischen virtuellen Adressen und Speicherseiten. Dies kann benutzt werden, um ein sehr effizientes realloc(3) zu implementieren.

Unter Linux ist der Speicher in Seiten eingeteilt. Ein Prozess verfügt über (ein oder) mehrere lineare virtuelle Speichersegmente. Jedes virtuelle Speichersegment hat ein oder mehr Mappings auf reale Speicherseiten (in der Seitentabelle). Jedes virtuelle Speichersegment hat seinen eigenen Schutz (Zugriffsrechte), welcher eine Segmentierungsverletzung (Segmentation violation, SIGSEGV) verursachen kann, wenn auf den Speicher nicht korrekt zugegriffen wird (z.B. beim Schreiben in ein schreibgeschütztes Segment). Zugreifen auf virtuellen Speicher außerhalb der Segmente verursacht ebenfalls eine Segmentierungsverletzung.

Falls mremap() dazu verwandt wird, einen mit mlock(2) oder Äquivalentem gesperrten Bereich zu verschieben oder zu erweitern, wird der Aufruf mremap() sich die beste Mühe geben, den neuen Bereich zu bestücken, wird aber nicht mit ENOMEM fehlschlagen, falls der Bereich nicht bestückt werden kann.

  Anwendungsfälle für MREMAP_DONTUNMAP
Mögliche Anwendungen für MREMAP_DONTUNMAP sind unter Anderem:

•   Nicht kooperatives userfaultfd(2): eine Anwendung kann sich einen virtuellen Adressbereich mittels MREMAP_DONTUNMAP schnappen und dann einen userfaultfd(2)-Handler einsetzen, um die Seitenausnahmebehandlungen zu handhaben, die nachfolgend auftreten, wenn andere Threads in dem Prozess auf Seiten in dem geschnappten Bereich zugreifen.

•   Automatische Speicherbereinigung: MREMAP_DONTUNMAP kann im Zusammenspiel mit userfaultfd(2) verwandt werden, um Algorithmen für automatische Speicherbereinigung zu implementieren (z.B. in einer virtuellen Java-Maschine). Eine solche Implementierung kann billiger (und einfacher) als konventionelle automatische Speicherbereinigungstechniken sein, die Seiten mit einem PROT_NONE-Schutz im Zusammenspiel mit dem SIGSEGV-Handler markieren, um Zugriff auf diese Seiten abzufangen.

FEHLER
Vor Linux 4.14 erstellte mremap() ein neues privates Mapping ohne Bezug zum ursprünglichen Mapping, falls alte_Größe Null war und das Mapping auf das alte_Adresse sich bezog, ein privates Mapping war (siehe die Beschreibung von MAP_PRIVATE in mmap(2)). Dieses Verhalten war nicht beabsichtigt und für Anwendungen im Benutzerbereich unerwartet (da es das Ziel von mremap() ist, ein neues Mapping basierend auf dem ursprünglichen Mapping zu erstellen). Seit Linux 4.14 schlägt mremap() in diesem Szenario mit dem Fehler EINVAL fehl.

SIEHE AUCH
brk(2), getpagesize(2), getrlimit(2), mlock(2), mmap(2), sbrk(2), malloc(3), realloc(3)

Ihr Lieblingsbuch über Betriebssysteme für weitere Informationen über »paged memory«. (Modern Operating Systems von Andrew S. Tanenbaum, Inside Linux von Randolph Bentson, The Design of the UNIX Operating System von Maurice J. Bach.)

ÜBERSETZUNG
Die deutsche Übersetzung dieser Handbuchseite wurde von Patrick Rother <krd@gulu.net>, Martin Eberhard Schauer <Martin.E.Schauer@gmx.de>, Mario Blättermann <mario.blaettermann@gmail.com> und Helge Kreutzmann <debian@helgefjell.de> erstellt.

Diese Übersetzung ist Freie Dokumentation; lesen Sie die GNU General Public License Version 3 (https://www.gnu.org/licenses/gpl-3.0.html) 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: debian-l10n-german@lists.debian.org.