Malloc-Implementierung?

Lesezeit: 8 Minuten

Benutzer-Avatar
Benutzer675257

Ich versuche es umzusetzen malloc und free für C, und ich bin mir nicht sicher, wie ich den Speicher wiederverwenden soll. Ich habe derzeit eine struct das sieht so aus:

typedef struct _mem_dictionary {
    void *addr;
    size_t size;
    int freed;
} mem_dictionary;

Mein malloc sieht aus wie das:

void *malloc(size_t size) {
    void *return_ptr = sbrk(size);
    if (dictionary == NULL) 
        dictionary = sbrk(1024 * sizeof(mem_dictionary));

    dictionary[dictionary_ct].addr = return_ptr;
    dictionary[dictionary_ct].size = size;
    dictionary[dictionary_ct].freed = 1;
    dictionary_ct++;

    return return_ptr;
}

Wenn ich Speicher freigebe, würde ich die Adresse einfach als markieren 0 (das würde bedeuten, dass es kostenlos ist). In meinem mallocwürde ich dann eine for-Schleife verwenden, um nach einem beliebigen Wert im Array zu suchen, der gleich ist 0 und weisen Sie dieser Adresse dann Speicher zu. Ich bin etwas verwirrt, wie ich das umsetzen soll.

Benutzer-Avatar
Sylvain Defresne

Der einfachste Weg, dies zu tun, besteht darin, eine verknüpfte Liste freier Blöcke zu führen. Im malloc, wenn die Liste nicht leer ist, suchen Sie nach einem Block, der groß genug ist, um die Anfrage zu erfüllen, und geben ihn zurück. Wenn die Liste leer ist oder kein solcher Block gefunden werden kann, rufen Sie auf sbrk etwas Speicher vom Betriebssystem zuzuweisen. in free, fügen Sie den Speicherblock einfach der Liste der freien Blöcke hinzu. Als Bonus können Sie versuchen, zusammenhängende freigegebene Blöcke zusammenzuführen, und Sie können die Richtlinie für die Auswahl des zurückzugebenden Blocks ändern (first fit, best fit, …). Sie können den Block auch teilen, wenn er größer als die Anfrage ist.

Einige Beispielimplementierungen (nicht getestet und offensichtlich nicht Thread-sicher, Verwendung auf eigene Gefahr):

typedef struct free_block {
    size_t size;
    struct free_block* next;
} free_block;

static free_block free_block_list_head = { 0, 0 };
static const size_t overhead = sizeof(size_t);
static const size_t align_to = 16;

void* malloc(size_t size) {
    size = (size + sizeof(size_t) + (align_to - 1)) & ~ (align_to - 1);
    free_block* block = free_block_list_head.next;
    free_block** head = &(free_block_list_head.next);
    while (block != 0) {
        if (block->size >= size) {
            *head = block->next;
            return ((char*)block) + sizeof(size_t);
        }
        head = &(block->next);
        block = block->next;
    }

    block = (free_block*)sbrk(size);
    block->size = size;

    return ((char*)block) + sizeof(size_t);
}

void free(void* ptr) {
    free_block* block = (free_block*)(((char*)ptr) - sizeof(size_t));
    block->next = free_block_list_head.next;
    free_block_list_head.next = block;
}

Notiz: (n + align_to - 1) & ~ (align_to - 1) ist ein Trick zum Runden n auf das nächste Vielfache von align_to das ist größer als n. Das funktioniert nur wann align_to ist eine Zweierpotenz und hängt von der binären Darstellung von Zahlen ab.

Wann align_to eine Zweierpotenz ist, hat es nur ein Bit gesetzt, und somit align_to - 1 hat alle niedrigsten Bitsätze (dh. align_to hat die Form 000…010…0, und align_to - 1 ist von der Form 000...001...1). Das bedeutet, dass ~ (align_to - 1) hat alle hohen Bits gesetzt und die niedrigen Bits nicht gesetzt (d.h. es hat die Form 111...110...0). So x & ~ (align_to - 1) werden alle niedrigen Bits von auf Null gesetzt x und auf das nächste Vielfache von abrunden align_to.

Zum Schluss hinzufügen align_to - 1 zu size stellen Sie sicher, dass wir auf das nächste Vielfache von aufrunden align_to (wenn nicht size ist schon ein Vielfaches von align_to in diesem Fall wollen wir bekommen size).

  • Was macht die Magie in der ersten Zeile der malloc-Funktion?

    – Igbanam

    4. November 2012 um 1:07 Uhr


  • Es rundet (size + sizeof(size_t)) auf das nächste Vielfache von align_to das ist größer als (size + sizeof(size_t)). Das funktioniert nur, wenn align_to ist eine Zweierpotenz.

    – Sylvain Defresne

    4. November 2012 um 19:19 Uhr

  • Eine ähnliche Technik, bei der ein Linked-List-Cache verwendet wird, um den zugewiesenen Speicher zu halten (anstatt ihn erneut zuzuweisen), um ein Grafikprogramm zu beschleunigen (das zu viel Zeit mit malloc verbringt), wird als Beispiel im ersten Teil von Spalte 9 verwendet : Code Tuning im Buch Programming Pearls von Jon Bentley. Das Buch enthält leider keinen Code in seinem Beispiel, daher war es für mich besonders nützlich, Code wie diesen zu sehen.

    – Erpel Sobania

    2. Februar 2015 um 1:14 Uhr


Benutzer-Avatar
Heide Hunnicutt

Sie möchten die nicht festlegen size Feld des Wörterbucheintrags auf Null setzen – Sie benötigen diese Informationen zur Wiederverwendung. Setzen Sie stattdessen freed=1 erst wenn der Block freigegeben wird.

Sie können benachbarte Blöcke nicht zusammenführen, da es möglicherweise zwischenzeitliche Aufrufe zu gab sbrk(), das macht es einfacher. Sie brauchen nur eine for Schleife, die nach einem ausreichend großen freigegebenen Block sucht:

typedef struct _mem_dictionary
{
    void *addr;
    size_t size;
    int freed;
} mem_dictionary;


void *malloc(size_t size)
{
     void *return_ptr = NULL;
     int i;

     if (dictionary == NULL) {
         dictionary = sbrk(1024 * sizeof(mem_dictionary));
         memset(dictionary, 0, 1024 * sizeof(mem_dictionary));
     }

     for (i = 0; i < dictionary_ct; i++)
         if (dictionary[i].size >= size
          && dictionary[i].freed)
     {
         dictionary[i].freed = 0;
         return dictionary[i].addr;
     }

     return_ptr = sbrk(size);

     dictionary[dictionary_ct].addr = return_ptr;
     dictionary[dictionary_ct].size = size;
     dictionary[dictionary_ct].freed = 0;
     dictionary_ct++;

     return return_ptr;
}

void free(void *ptr)
{
    int i;

    if (!dictionary)
        return;

    for (i = 0; i < dictionary_ct; i++ )
    {
        if (dictionary[i].addr == ptr)
        {
            dictionary[i].freed = 1;
            return;
        }
    }
}

Das ist nicht großartig malloc() Implementierung. Tatsächlich die meisten malloc/free Implementierungen weisen jedem von malloc zurückgegebenen Block einen kleinen Header zu. Der Header könnte an der Adresse acht (8) Bytes beginnen weniger als beispielsweise der zurückgegebene Zeiger. In diesen Bytes können Sie einen Zeiger auf die speichern mem_dictionary Eintrag, der den Block besitzt. Dies vermeidet die O(N)-Operation in free. Sie können das O(N) in vermeiden malloc() durch Implementieren einer Prioritätswarteschlange von freigegebenen Blöcken. Erwägen Sie die Verwendung eines Binoms Haufenmit Blockgröße als Index.

  • Entschuldigung, ich bin relativ neu in C, aber was ist die Dictionary-Variable in malloc()?

    – nein92

    11. September 2013 um 10:46 Uhr


  • @ no92 – Ich hätte dieses “Journal” statt “Wörterbuch” nennen sollen. Denken Sie daran, das ist mein Beispiel und triviale Implementierung von malloc. Es hat mindestens einen offensichtlichen Fehler: Es können nie mehr als 1024 Blöcke gleichzeitig zugewiesen werden. Der einzige Zweck dieses Beispiels ist es, dem Leser a Startpunkt für die eigene Umsetzung malloc. Dies ist nur eine konzeptionelle Grundlage für die Implementierung von a malloc/free Bibliothek. Es wird nicht einmal umgesetzt realloc als ein weiterer eklatanter Mangel. Es ist vielleicht nicht einmal das beste Beispiel. 🙂

    – Heide Hunnicutt

    12. September 2013 um 17:12 Uhr

Ich leihe mir Code aus Sylvains Antwort. Er scheint es versäumt zu haben, die Größe der free_block*-ini bei der Berechnung des Overheads zu berechnen.

Insgesamt funktioniert der Code, indem dieser free_block als Header dem zugewiesenen Speicher vorangestellt wird. 1. Wenn der Benutzer malloc aufruft, gibt malloc die Adresse der Nutzlast direkt nach diesem Header zurück. 2. Wenn free aufgerufen wird, wird die Anfangsadresse des Headers für den Block berechnet (durch Subtrahieren der Headergröße von der Blockadresse) und zum freien Blockpool hinzugefügt.

typedef struct free_block {
    size_t size;
    struct free_block* next;
} free_block;

static free_block free_block_list_head = { 0, 0 };

// static const size_t overhead = sizeof(size_t);

static const size_t align_to = 16;

void* malloc(size_t size) {
    size = (size + sizeof(free_block) + (align_to - 1)) & ~ (align_to - 1);
    free_block* block = free_block_list_head.next;
    free_block** head = &(free_block_list_head.next);
    while (block != 0) {
        if (block->size >= size) {
            *head = block->next;
            return ((char*)block) + sizeof(free_block);
        }
        head = &(block->next);
        block = block->next;
    }

    block = (free_block*)sbrk(size);
    block->size = size;

    return ((char*)block) + sizeof(free_block);
}

void free(void* ptr) {
    free_block* block = (free_block*)(((char*)ptr) - sizeof(free_block ));
    block->next = free_block_list_head.next;
    free_block_list_head.next = block;
}

  • Danke, ich denke, diese Antwort ist etwas korrekter als Sylvains Antwort, da ich mich nur darüber gewundert habe. Die Overhead-Variable ist eine sehr gute Idee, nur nicht richtig berechnet oder gar verwendet.

    – bbill

    18. Oktober 2014 um 22:05 Uhr

  • Kann mir jemand sagen, wie man Head in der Malloc-Funktion verwendet? (free_block** head = &(free_block_list_head.next);) Ich sehe es nirgendwo verwendet. Außerdem, warum fügen wir hinzu sizeof(free_block) Vor der Rückkehr?

    – Benutzer1719160

    8. März 2016 um 6:22 Uhr


  • head ist ein Zeiger auf eine Adresse, die einen Zeiger enthält. Es wird in der While-Schleife verwendet, um den an den Benutzer zurückgegebenen Speicherblock zu entkoppeln. Addieren und Subtrahieren sizeof(free_block) ist ein gängiger und netter Trick, um die Metadaten vor dem Aufrufer zu „verstecken“.

    – Björn Lindqvist

    13. April 2016 um 4:34 Uhr

  • Es gibt auch einen kleinen Fehler in der free()-Implementierung as free(NULL) wird segfault.

    – Björn Lindqvist

    13. April 2016 um 4:35 Uhr

  • Hallo, können Sie das Passende hinzufügen realloc Funktion für diese Implementierung?

    – Philipp

    30. Juli 2018 um 10:11 Uhr

1379820cookie-checkMalloc-Implementierung?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy