web-dev-qa-db-de.com

Wie wird malloc () intern implementiert?

Kann jemand erklären, wie malloc() intern funktioniert?

Ich habe manchmal strace program Ausgeführt und sehe eine Menge von sbrk Systemaufrufen, wobei man sbrk Gespräche darüber geführt haben, dass es in malloc() verwendet wird, aber nicht viel mehr.

108
bodacydo

Der Systemaufruf sbrk verschiebt den "Rand" des Datensegments. Dies bedeutet, dass es einen Rand eines Bereichs verschiebt, in dem ein Programm Daten lesen/schreiben kann (wobei es wachsen oder schrumpfen kann, obwohl AFAIK no malloc dem Kernel mit dieser Methode wirklich Speichersegmente zurückgibt). Abgesehen davon gibt es auch mmap, das zum Zuordnen von Dateien in den Arbeitsspeicher, aber auch zum Zuweisen von Arbeitsspeicher verwendet wird (wenn Sie gemeinsam genutzten Arbeitsspeicher zuweisen müssen, verwenden Sie mmap, um dies zu tun).

Sie haben also zwei Möglichkeiten, um mehr Speicher vom Kernel zu bekommen: sbrk und mmap. Es gibt verschiedene Strategien, wie Sie den Speicher organisieren können, den Sie vom Kernel haben.

Ein naiver Weg besteht darin, es in Zonen zu unterteilen, die oft als "Eimer" bezeichnet werden und bestimmten Strukturgrößen gewidmet sind. Beispielsweise könnte eine malloc -Implementierung Buckets für 16-, 64-, 256- und 1024-Byte-Strukturen erstellen. Wenn Sie malloc bitten, Ihnen Speicher einer bestimmten Größe zu geben, rundet es diese Zahl auf die nächste Bucket-Größe und gibt Ihnen dann ein Element aus diesem Bucket. Wenn Sie einen größeren Bereich benötigen, kann mallocmmap zur direkten Zuordnung zum Kernel verwendet werden. Wenn der Eimer einer bestimmten Größe leer ist, kann mallocsbrk verwenden, um mehr Platz für einen neuen Eimer zu erhalten.

Es gibt verschiedene malloc Designs und es gibt wahrscheinlich keine echte Möglichkeit, malloc zu implementieren, da Sie einen Kompromiss zwischen Geschwindigkeit, Overhead und Vermeidung von Fragmentierung/Raumeffektivität eingehen müssen. Wenn einem Bucket beispielsweise die Elemente ausgehen, kann eine Implementierung ein Element aus einem größeren Bucket abrufen. Teilen Sie es auf und fügen Sie es dem Bucket hinzu, dem die Elemente ausgehen. Dies wäre recht platzsparend, aber nicht bei jedem Design möglich. Wenn Sie nur einen weiteren Bucket über sbrk/mmap erhalten, ist dies möglicherweise schneller und noch einfacher, aber nicht so platzsparend. Auch das Design muss natürlich berücksichtigen, dass "free" irgendwie wieder Platz für malloc zur Verfügung stellen muss. Sie verteilen Speicher nicht einfach, ohne ihn wiederzuverwenden.

Wenn Sie interessiert sind, hat der OpenSER/Kamailio SIP= Proxy zwei malloc Implementierungen (sie benötigen ihre eigenen, da sie den gemeinsamen Speicher und das System malloc unterstützt keinen gemeinsamen Speicher.) Siehe: https://github.com/OpenSIPS/opensips/tree/master/mem

Dann könnten Sie sich auch die GNU libc malloc Implementierung ansehen, aber diese ist sehr kompliziert, IIRC.

100
DarkDust

Einfach malloc und freie Arbeit wie folgt:

malloc bietet Zugriff auf den Heap eines Prozesses. Der Heap ist ein Konstrukt in der C-Core-Bibliothek (üblicherweise libc), mit dem Objekte exklusiven Zugriff auf Speicherplatz auf dem Heap des Prozesses erhalten.

Jede Zuordnung auf dem Heap wird als Heapzelle bezeichnet. Dies besteht normalerweise aus einem Header, der Informationen zur Größe der Zelle sowie einen Zeiger auf die nächste Heap-Zelle enthält. Dies macht einen Heap effektiv zu einer verknüpften Liste.

Wenn ein Prozess gestartet wird, enthält der Heap eine einzelne Zelle, die den gesamten beim Start zugewiesenen Heapspeicher enthält. Diese Zelle befindet sich auf der freien Liste des Heaps.

Wenn man malloc aufruft, wird Speicher aus der großen Heap-Zelle entnommen, die von malloc zurückgegeben wird. Der Rest wird zu einer neuen Heap-Zelle geformt, die aus dem gesamten Rest des Speichers besteht.

Wenn Speicher freigegeben wird, wird die Heap-Zelle an das Ende der freien Liste des Heaps angefügt. Nachfolgende Mallocs durchlaufen die freie Liste und suchen nach einer Zelle geeigneter Größe.

Wie zu erwarten ist, kann der Heap fragmentiert werden, und der Heap-Manager kann von Zeit zu Zeit versuchen, benachbarte Heap-Zellen zusammenzuführen.

Wenn für eine gewünschte Zuweisung kein Speicher mehr auf der freien Liste vorhanden ist, ruft malloc brk oder sbrk auf, die Systemaufrufe, die weitere Speicherseiten vom Betriebssystem anfordern.

Nun gibt es einige Änderungen, um die Heap-Operationen zu optimieren.

  • Bei großen Speicherzuordnungen (normalerweise> 512 Byte) wechselt der Heap-Manager möglicherweise direkt zum Betriebssystem und weist eine vollständige Speicherseite zu.
  • Der Heap kann eine Mindestgröße für die Zuweisung angeben, um große Fragmentierungsmengen zu verhindern.
  • Der Heap kann sich auch in Bins für kleine Zuordnungen und für größere Zuordnungen aufteilen, um größere Zuordnungen schneller zu machen.
  • Es gibt auch clevere Mechanismen zur Optimierung der Multithread-Heap-Zuordnung.
45
doron

Es ist auch wichtig zu wissen, dass das einfache Bewegen des Programmunterbrechungszeigers mit brk und sbrk nicht wirklich zuordnet Im Speicher wird lediglich der Adressraum eingerichtet. Unter Linux beispielsweise wird der Speicher beim Zugriff auf diesen Adressbereich durch tatsächliche physische Seiten "gesichert", was zu einem Seitenfehler führt und schließlich dazu, dass der Kernel den Seitenzuweiser aufruft, um eine Hintergrundseite abzurufen.

7
mgalgs