/* 19Jun24: Negativ-Beispiel (III) einer Stack Implementierung. * * Anstatt selbst den Speicher zu verwalten, übergeben wir die Aufgabe * hier an das Betriebssystem. Hierzu benutzen wir den Systemaufruf * mmap(2), welches den logischen Adressraum erweitert und eine neue * Adressenbereich gültig macht. ACHTUNG: In der Aufgabe 5 soll * mmap(2) nicht so benutzt werden, sondern um den Speicherbereicht * EINMAL am ANFANG zu besorgen, der dann intern verwaltet wird. * * Vorteile sind, dass wir beliebig viel Speicher anfordern können, * insofern unser Adressraum noch erweitert werden kann. * * Nachteile sind, dass mmap(2), wie alle Systemaufrufe relativ teuer * sind, und dass der Speicher nicht so fein Vergeben werden kann, wie * wenn wir die Speicherverwaltung im Userspace umsetzen (wie es ja in * der Aufgabe gedacht ist). Daher, wie in der TÜ gesehen wurde, * läuft uns sehr schnell der Speicher aus. */ /* -> Siehe Kommentare in halde-stack.c */ #define _GNU_SOURCE #include "halde.h" #include #include #include #include #include #include #include #define SIZE (1024*1024*1) /* Wir merken uns am Anfang von einem Speicherbereich, wie viel groß * das vergebene Segment ist. Dieses stellen wir in einem struct dar, * welches ein "Flexible-Array-Member" einsetzt. Dieses ist * Syntax-Zucker, um auf den Speicher /hinter/ dem Stack zu verweisen. * Damit können wir die administrativen Daten direkt vor dem * vergebenem Speicher sichern (daher ist es ja Gefährlich außerhalb * vom vergebenem Puffer zu schreiben). */ struct mblock { size_t size; char memory[]; }; void *malloc(size_t size) { struct mblock *mblock; /* Hier die Reduktion, auf das grobe mmap(2). Wenn man die * vergebenen Zeiger anschaut, sieht man dass auf einem x86_64 * System immer alle Zeiger 4k weit weg voneinander sind, weil * immer eine neue Seite angelegt wird. Die Argumente (aus den * Folien) sind ansonsten für uns nicht wichtig, man kann auch * diesen Aufruf mehr oder weniger direkt in der Aufgabe * benutzen. */ mblock = mmap(NULL, sizeof (struct mblock) + size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); /* Hier ist darauf zu achten, dass die Fehlerbehandlung von * mmap(2) NICHT das gleiche macht wie malloc(3)! */ if (mblock == MAP_FAILED) { return NULL; } /* Wir merken uns wie viel Speicher in mblock->memory gültig * sind, damit wir auch später free(3) implementieren können. */ mblock->size = size; return mblock->memory; /* Alternativ: ((char*) mblock) + sizeof mblock */ } void free(void *ptr) { /* Free(3) bekommt als Argument einen Wert der von malloc(3) * zurückgeben wurde (oder NULL), ... */ if (ptr == NULL) return; /* Um aus `ptr' den Block zu berechnen, müssen wir /hinter/ dem * Zeiger schauen, und hoffen da ein "struct mblock" zu * finden. Wir bestimmen diese Speicherstelle, mit Pointer-Arithmetik: * * ((struct mblock*)ptr) - 1 * ((char*)ptr) - sizeof (struct mblock) * | * | ptr * | | * v v * +---+---+---+---+---+---+---+---+---+---+---+... * | | | | | | | | | | | | interpretiert als char* * +---+---+---+---+---+---+---+---+---+---+---+... * * +---------------+----------------------------... * | size | memory interpretiert als struct mblock* * +---------------+----------------------------... * \_______________/ * sizeof (struct mblock), merke dass memory nicht dazu zählt! * * Wenn wir also ein "struct mblock" nach hinten "schauen" können * wir `size' herauslesen. Hier ist aber keine Garantie, dass es * wirklich ein gültiger "struct mblock" ist. Stattdessen prüfen * wir ob es sich wirklich von uns vergebenem Speicher handelt, * indem wir den Rückgabewert von munmap(2) überprüfen. */ struct mblock *mblock = ptr; mblock--; /* Das geht über SP hinaus: Damit wir den logischen Adressraum * verkleinern, können wir munmap(2) benutzen. Das braucht eben * die Größe angeforderten Speichers, was wir aus dem "struct * mblock" herauslesen. Hier erwähne ich es nur aus * Vollständigkeit, es hat keinen weiteren Einfluss auf die * Aufgabe. */ if (-1 == munmap(ptr, mblock->size)) { abort(); } } /**********************************************************************/ /* ACHTUNG: Diese Datei enthält keine Implementierung von calloc(3) */ /* und realloc(3). Diese Funktionen sind jedoch für die Aufgabe 5 zu */ /* implementieren. Programme welche man gegen dieses Modul bindet */ /* könnten sich möglicherweise Falsch verhalten, wenn es unsere */ /* `malloc' und realloc(3) kombinierend! */ /**********************************************************************/