HOWTO · C

Verwenden Sie die mmap-Funktion, um in C in den Speicher zu schreiben

Dieser Artikel informiert über die Funktion mmap() und zeigt, wie wir die Funktion verwenden können, um in den Speicher in C zu schreiben.

Auf dieser Seite

mmap() erstellt eine Speicherabbildung in einem Prozess. Eine beschreibbare anonyme Abbildung lässt sich als Speicher für C-Daten verwenden; eine gemeinsam genutzte anonyme Abbildung kann Änderungen zwischen verwandten Prozessen übertragen. Diese Anleitung erläutert die sechs Argumente, zeigt, wie sich Fehler beim Erstellen einer Abbildung erkennen lassen, und erklärt, wie sie mit munmap() freigegeben wird.

Was mmap() bewirkt und zurückgibt

Eine Speicherabbildung verknüpft einen Bereich des virtuellen Adressraums eines Prozesses mit einer Datei, einem Gerät oder anonymem Speicher. Anonymer Speicher ist nicht durch eine Datei hinterlegt; Änderungen daran werden daher nicht in einer Datei gespeichert. Binden Sie <sys/mman.h> ein, um mmap() und die zugehörigen Konstanten zu verwenden.

Die Funktion nimmt sechs Argumente entgegen und gibt bei Erfolg die Adresse der neuen Abbildung zurück. Bei einem Fehler gibt sie MAP_FAILED zurück. Vergleichen Sie den Rückgabewert daher mit diesem speziellen Wert, statt auf NULL oder null zu prüfen. Der Aufruf kann außerdem errno setzen, was bei der Diagnose eines Fehlers helfen kann.

Die Parameter und Flags von mmap() verstehen

Die Argumente lauten der Reihe nach address, length, prot, flags, fd und offset:

  • address ist eine vorgeschlagene Startadresse. Mit NULL kann der Kernel eine geeignete Adresse auswählen; Anwendungen überlassen ihm dies üblicherweise.
  • length gibt die Anzahl der abzubildenden Bytes an. Der Kernel berücksichtigt die Seitenausrichtung des abgebildeten Bereichs. Verwenden Sie beim Freigeben dennoch durchgehend die angeforderte Länge.
  • prot legt die Zugriffsrechte fest. Kombinieren Sie PROT_READ und PROT_WRITE für Daten, die gelesen und geändert werden sollen. Fügen Sie PROT_EXEC nur hinzu, wenn ausführbarer Speicher benötigt wird.
  • flags legen fest, wie die Abbildung gemeinsam genutzt und hinterlegt wird. MAP_PRIVATE hält Änderungen auf den jeweiligen Prozess beschränkt; MAP_SHARED macht sie über die gemeinsame Abbildung sichtbar. MAP_ANONYMOUS erstellt Speicher ohne Dateihinterlegung. Dieses Flag wird üblicherweise mit einem der Flags für die gemeinsame Nutzung kombiniert.
  • fd bezeichnet bei einer dateigestützten Abbildung eine Datei. Bei MAP_ANONYMOUS wird der Deskriptor auf gängigen Unix-ähnlichen Systemen ignoriert; Beispiele verwenden oft -1.
  • offset bestimmt das Startbyte in einer Datei. Bei dateigestützten Abbildungen muss dieser Wert ein Vielfaches der Systemseitengröße sein. Für eine anonyme Abbildung verwenden Sie null.

Vermeiden Sie MAP_FIXED, sofern Sie keinen konkreten und sorgfältig kontrollierten Bedarf an einer bestimmten Adresse im Adressraum haben: Das Flag kann vorhandene Abbildungen im angegebenen Bereich ersetzen. Diese Flags und fork() sind auf vielen Unix-ähnlichen Systemen verfügbar, jedoch nicht in jeder C-Umgebung.

In anonymen Speicher mit mmap() schreiben

Dieses vollständige Beispiel bildet Speicherplatz für fünf Ganzzahlen ab, schreibt die Werte, liest sie aus und gibt anschließend genau den angeforderten Bereich frei. Es setzt ein Unix-ähnliches System mit Unterstützung für MAP_ANONYMOUS voraus. Bei erfolgreicher Ausführung gibt es die unten gezeigte Ausgabe aus.

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

#include <stdio.h>
#include <sys/mman.h>

int main(void) {
    const size_t count = 5;
    const size_t length = count * sizeof(int);
    int *values = mmap(NULL, length, PROT_READ | PROT_WRITE,
                       MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    if (values == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    for (size_t i = 0; i < count; ++i) {
        values[i] = (int)(i * 10);
    }
    for (size_t i = 0; i < count; ++i) {
        printf("[%d] ", values[i]);
    }
    putchar('\n');

    if (munmap(values, length) == -1) {
        perror("munmap");
        return 1;
    }
    return 0;
}
[0] [10] [20] [30] [40]

Die Abbildung ist beschreibbar, weil ihre Schutzrechte PROT_READ und PROT_WRITE kombinieren. MAP_PRIVATE eignet sich hier, da kein anderer Prozess die Schreibvorgänge sehen muss. munmap() benötigt die Adresse der Abbildung und dieselbe angeforderte Länge; die Anzahl der Elemente genügt nicht. Das Beispiel gibt die Werte mit Klammern aus; die Zahlen entsprechen den geschriebenen Werten. Zeichenketten, die bei Fehlern erscheinen können, bleiben im unveränderten Beispielcode Englisch.

Eine anonyme Abbildung zwischen Prozessen gemeinsam nutzen

Eine mit MAP_SHARED erstellte Abbildung kann Änderungen über fork() hinweg übertragen. Anschließend greifen Kind- und Elternprozess auf denselben abgebildeten Bereich zu. Das Beispiel leert den Ausgabepuffer vor fork(), damit die erste Zeile bei umgeleiteter Ausgabe nicht doppelt erscheint. Der Elternprozess wartet auf das Kind, bevor er dessen Änderungen liest. Wie das vorherige Beispiel setzt auch dieses Unix-ähnliche Unterstützung für MAP_ANONYMOUS und fork() voraus. Die bereitgestellte Ausführung erzeugte die unten gezeigte Ausgabe.

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>

int main(void) {
    const size_t count = 5;
    const size_t length = count * sizeof(int);
    int *values = mmap(NULL, length, PROT_READ | PROT_WRITE,
                       MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (values == MAP_FAILED) {
        perror("mmap");
        return 1;
    }

    for (size_t i = 0; i < count; ++i) {
        values[i] = (int)(i + 7);
    }
    printf("Initial values:");
    for (size_t i = 0; i < count; ++i) {
        printf(" %d", values[i]);
    }
    putchar('\n');
    fflush(stdout);

    pid_t child_pid = fork();
    if (child_pid == -1) {
        perror("fork");
        munmap(values, length);
        return 1;
    }
    if (child_pid == 0) {
        for (size_t i = 0; i < count; ++i) {
            values[i] *= 5;
        }
        if (munmap(values, length) == -1) {
            _exit(1);
        }
        _exit(0);
    }

    int status;
    if (waitpid(child_pid, &status, 0) == -1) {
        perror("waitpid");
        munmap(values, length);
        return 1;
    }
    if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
        fputs("child process failed\n", stderr);
        munmap(values, length);
        return 1;
    }

    printf("Updated values:");
    for (size_t i = 0; i < count; ++i) {
        printf(" %d", values[i]);
    }
    putchar('\n');

    if (munmap(values, length) == -1) {
        perror("munmap");
        return 1;
    }
    return 0;
}
Initial values: 7 8 9 10 11
Updated values: 35 40 45 50 55

Das Kind schreibt in die gemeinsam genutzte Abbildung und wird beendet. Sobald waitpid() abgeschlossen ist, liest der Elternprozess dieselben Änderungen. Eine Abbildung mit MAP_PRIVATE würde dieses gemeinsame Nutzungsverhalten nicht ermöglichen. Beide Prozesse geben ihre jeweilige Abbildung vor dem Beenden frei. Bei dateigestützten Abbildungen kann MAP_SHARED Änderungen über die Abbildung sichtbar machen; die dauerhafte Speicherung in der Datei hängt jedoch zusätzlich von Synchronisierung und dem Dateisystem ab. Die Ausgabebezeichnungen „Initial values:“ und „Updated values:“ bleiben im bytegleich übernommenen Beispielcode Englisch.