Hallo C-Cracks,
Ich habe folgendes Problem. Ich habe eine Bibliothek, die mit malloc()
sehr viele sehr kleine Speicherbereiche alloziiert. In meiner Software
nutze ich auch malloc(). Nun benötige ich die Daten der Bibliothek nur
temporär und nach einem free() habe ich dann viele kleine
Speicherbereiche, die nicht an das Betriebssystem zurückgegeben werden
können.
Ich suche nun eine Implementierung von malloc() und free(), die ich in
dieser Bibliothek verwenden möchte. Ich bin bei meinen Recherchen auf
MMalloc (GNU memory-mapped malloc) gestoßen, welches für mich ideal
erscheint. Leider habe ich diese Software nirgends gefunden. Weiß
jemand, was damit passiert ist? Kennt jemand Alternativen?
mfg, Stefan.
Alternative wäre, zu Beginn einen ausreichend großen Speicherbereich mit
Standard-malloc() anzufordern und dann innerhalb dieses Bereiches mit
einem eigenen malloc() (kann prinzipiell auf dem Quelltext des
Standard-malloc()s basieren) Speicherstücke zuzuweisen. Hat den Vorteil,
dass das Betriebssystem nur einen einzigen Riesenblock verwaltet, der
dann logischerweise nix mehr fragmentiert.
Stefan May wrote:
> Nun benötige ich die Daten der Bibliothek nur> temporär und nach einem free() habe ich dann viele kleine> Speicherbereiche, die nicht an das Betriebssystem zurückgegeben werden> können.
Woraus schlussfolgerst du dies, und auf welchem System arbeitest du
überhaupt?
Jedes vernünftige malloc() sollte Blöcke wieder zusammenfassen können,
um später neu daraus zu allozieren. Eine Rückgabe an das Betriebssystem
war lange Zeit nicht implementiert, später wurde sie es in der Regel,
aber dafür ist sie bei heutigen VM-basierten Systemen oft genug kaum
wirklich sinnvoll. Im Zweifelsfalle wird dadurch lediglich die
Belastung des swap reduziert.
Hallo Jörg,
Sorry, das war natürlich wichtig. Betriebssystem ist Linux Kernel
2.6.18, libc6 2.3.6. Meine Schlußfolgerung ziehe ich aus der Größe des
Prozesses in top und aus Durchläufen mit dem Memory-Profiler massif in
Valgrind. Eine Grafik habe ich mal angehängt. Trotz der Freigabe des
Speicher durch free, zu sehen in der Grafik, ändert sich die Größe in
top nicht.
Auf dem System habe ich leider keinen Swap. Den kann ich auch nicht
hinzufügen, weil die Maschine nur eine CF-Karte als Massenspeicher
besitzt. Mehr RAM ist leider keine Option, da es sich um so einen blöden
Industriecomputer ohne Aufrüstmöglichkeiten handelt.
Das Zusammenfassen von großen Blöcken durch malloc() nützt mir leider
gar nichts, da ich es in meiner Software gar nicht brauche. Ich benötige
den Speicher nur kurzfristig während der Start-Phase.
mfg, Stefan.
Stefan May wrote:
> Trotz der Freigabe des> Speicher durch free, zu sehen in der Grafik, ändert sich die Größe in> top nicht.
Was ja letztlich eine virtuelle Größe ist, klar. VM-Systeme
funktionieren so richtig nur mit Swap.
Du kannst dir mal das "phkmalloc" vom FreeBSD angucken. Meines Wissens
gibt das in der Tat VM ans System zurück. Ich habe es mit paar Tweaks
schon erfolgreich unter Linux compiliert, um damit eine memory
corruption in einem Python-Modul (der auch nur mittels Swig automatisch
generiert war) zu debuggen. phkmalloc besitzt dafür ein paar nützliche
Fietschers wie die Möglichkeit, allen allozierten Speicher mit einem
Bitmuster zuzumüllen (sodass nicht ,aus Versehen' hinterher alles 0
ist und dann der Test auf einen Nullzeiger ungewollt einen nicht
initialisierten Zeiger überspringen kann) und einen abort() beim
Auftreten der ersten Unstimmigkeit zu erzwingen. Natürlich alles
optional, wobei man die Optionen auf drei verschiedenen Wege spezifi-
zieren kann.
Speicher an das System zurückgeben...
Na ja, das funktioniert unter Unix nicht so richtig (ist aber
normalerweise auch nicht notwendig...).
Grund ist, daß unter Unix (und ähnlichen Systemen) bei einer
Speicheranforderung an das Betriebssystem (mittels brk) lediglich die
Speichergrenze in den Heap für den Prozess verschoben wird (wir haben ja
virtuelle Speicherverwaltung). Um nun Speicher an das System
zurückzugeben, muß diese Grenze wieder erniedrigt werden. Das geht aber
nur, wenn der Prozess nicht inzwischen dahinter Speicher alloziert hat
(im groben heißt das, daß der zuletzt allozierte Speicher auch zuerst
freigegebn wird - falls er nicht in Lücken gefüllt werden konnte).
Manche Unixe, z.B. auch HP-UX haben das implementiert, allerdings hat
das nur mit speziell dafür gestrickter Software (manchmal) Erfolg....
Einfacher ist es, den Swap zu erhöhen, dann landet der freigegebene,
aber nicht mehr gebrauchte Speicher dort und kostet nur Plattenplatz...
Wenn man wirklich große Stücke Speicher braucht und wieder freigeben
will, dann geht man so vor wie im Originalposting beschrieben - Speicher
als memory map holen und freigeben. Ich wusste bisher gar nicht, das es
das als library gibt, ich hätte das Rad neu erfunden...
Link für mmalloc erggogelt z.B.
http://www.geocities.com/SiliconValley/Circuit/5426/mmalloc.html
Hallo Berhard,
Das von Dir gefundene mmalloc ist leider nicht das, was ich meinte. Ich
habe per Google nur ein PDF gefunden:
http://www.gnuarm.com/pdf/mmalloc.pdf
Trotzdem ist Dein mmalloc interessant, wenn auch der Code ziemlich alt
aussieht (Mitte 1998, also voriges Jahrtausend :-)
mfg, Stefan.
Stefan May wrote:
> Trotzdem ist Dein mmalloc interessant, wenn auch der Code ziemlich alt> aussieht (Mitte 1998, also voriges Jahrtausend :-)
Ja, aber die Funktionalität in Unix ist ja viel älter, und erprobt. D.h.
auch zehn Jahre alter Code ist heute noch gültig...
Bernhard M. wrote:
> Das geht aber> nur, wenn der Prozess nicht inzwischen dahinter Speicher alloziert hat> (im groben heißt das, daß der zuletzt allozierte Speicher auch zuerst> freigegebn wird - falls er nicht in Lücken gefüllt werden konnte).
Wenn er aber, wie er schreibt, nach der Initialisierung allen Speicher
wieder freigegeben hat, sollte das funktionieren.
phkmalloc jedenfalls kann das. Hier ein kleines FreeBSD-Beispiel:
1
#include<sys/types.h>
2
#include<stdio.h>
3
#include<unistd.h>
4
5
void*p1,*p2;
6
7
int
8
main(void)
9
{
10
printf("Initial break value: %p\n",sbrk(0));
11
p1=malloc(100000);
12
printf("2nd break value: %p\n",sbrk(0));
13
p2=malloc(200000);
14
printf("3rd break value: %p\n",sbrk(0));
15
free(p1);
16
printf("4th break value: %p\n",sbrk(0));
17
free(p2);
18
printf("5th break value: %p\n",sbrk(0));
19
20
return0;
21
}
Ausgabe:
1
Initial break value: 0x8049918
2
2nd break value: 0x8065000
3
3rd break value: 0x8096000
4
4th break value: 0x8096000
5
5th break value: 0x805c000
Dass der letzte Wert hier nicht gleich dem ersten ist dürfte daran
liegen, dass zwischenzeitlich stdio-Puffer hinzu gekommen sind, die
nicht freigegeben werden können. Aber das sollte dem OP ja nicht
passieren. Es ist deutlich erkennbar, dass Speicher ans System
zurück gegeben worden ist.
Auch phkmalloc ist übrigens von 1994... Aber OK, ich sehe im CVS-Log,
dass es seither doch noch einige Veränderungen erfahren hat.
@haku: Ja, Du täuscht Dich. Die GNU-libc nutzt zwar mmap. Nach dem oben
geposteten PDF kann man aber Speicher-Pools angeben.
Ich bin das Problem jetzt anders angegangen. Ich habe meinen vorhandenen
Code neu geschrieben, so daß er nicht mehr viele kleine Blöcke mit
malloc holt. Meine Datenstruktur hat jetzt eine konstante Größe und ich
reserviere mir immer gleich eine recht große Zahl. Diese verwalte ich
dann allein. Der Vorteil: ich muß keinen eigenen, vielleicht kaputten
Memory-Allocator verwenden. Und zwei ich nutze das GNU-libc malloc aus,
welches bei großen Chunks mit mmap arbeitet.
@Jörg: phkmalloc habe ich ausprobiert und es hat relativ gut
funktioniert. Bis auf die mir unerklärlichen Segmentation-faults.... ;-)
Ich habe dann die beschriebene Lösung favorisiert. Leider reichen meine
C/Unix-Kenntnisse an der Stelle nicht aus.
Vielen Dank für Eure Mithilfe. Ich habe eine Menge über
Speichermanagement gelernt. Mann, ich bin froh, daß ich mich in Java
darum nicht kümmern muß. :-)
mfg, Stefan.
Stefan May wrote:
> @Jörg: phkmalloc habe ich ausprobiert und es hat relativ gut> funktioniert. Bis auf die mir unerklärlichen Segmentation-faults.... ;-)
Debug-Optionen anwerfen und die Probleme suchen... Zu 99,9 % liegen
sie in deinem Code...
Wie gesagt, ich hab's auch unter Linux als memory allocator für ein
Python schon benutzt, und nachdem ich damit den off-by-one-error
in besagtem Swig-Modul gefunden habe (den ich mit dem originalen
GNU-malloc nie wirklich einkreisen konnte), war es völlig stabil.
Hallo Jörg,
Ich habe keinen anderen Memory-Allocator verwendet, weil das nur die
Symptome bekämpfen würde. Der Code ist sehr ungeschickt, da tut eine
Refaktorisierung ganz gut.
Den Fehler bei der Verwendung von phkmalloc habe ich auch schon
gefunden. Deine Vermutung war natürlich richtig, wie zu erwarten lag das
Problem in meinem Code.
Wenn das Forum erlaubt, mißbrauche ich Euch mal als seelischen
Mülleimer: AAAAHHH, WIE KANN MAN NUR SO MIESEN CODE
SCHREIBEN!!!!!1!!1ELF!!
mfg, Stefan.
Stefan May wrote:
> Wenn das Forum erlaubt, mißbrauche ich Euch mal als seelischen> Mülleimer: AAAAHHH, WIE KANN MAN NUR SO MIESEN CODE> SCHREIBEN!!!!!1!!1ELF!!
Nun, wenn du dich jetzt wohler fühlst, sei dir das gestattet. :-))
Freut mich, dass du's gefunden hast und jetzt hoffentlich mit dem
Resultat zufrieden bist.