Forum: Compiler & IDEs malloc(): mit gcc-arm-none-eabi irgendwie anders


von Mehmet K. (mkmk)


Lesenswert?

Servus allerseits

Wenn ich auf dem PC mit malloc arbeite, dann verhaelt es sich so, wie 
man es erwartet: der freigegebene Speicherplatz wird bei der naechsten 
Anforderung wiederverwendet.

PC:
1
    static char *ptr_128, *ptr_64, *ptr_32;
2
3
    ptr_128 = (char *)malloc(128);  // 0x3d2d40
4
    ptr_64  = (char *)malloc( 64);  // 0x3d2e30
5
    ptr_32  = (char *)malloc( 32);  // 0x3d2dd8
6
7
    free(ptr_128);
8
    free(ptr_64);
9
    free(ptr_32);
10
11
    ptr_32  = (char *)malloc( 32);  // 0x3d2d40 <-- korrekt
12
    ptr_128 = (char *)malloc(128);  
13
    free(ptr_128);
14
    free(ptr_32);
15
    ptr_128 = (char *)malloc(128); // 0x3d2d40 <-- korrekt
16
    free(ptr_128);

Wenn ich mit gcc-arm-none-eabi (4.7.3) arbeite, habe ich ein 
Verstaendnis-Problem. Der Speicherplatz wird nicht gleich 
wiederverwendet, sondern erst irgendwann mal spaeter (habe diesbezüglich 
einen laengeren Test als diesen hier gemacht).
1
    static char *ptr_128, *ptr_64, *ptr_32;
2
3
    ptr_128 = (char *)malloc(128);  // 0x200045b0
4
    ptr_64  = (char *)malloc( 64);  // 0x20004638
5
    ptr_32  = (char *)malloc( 32);  // 0x20004680
6
7
    free(ptr_128);
8
    free(ptr_64);
9
    free(ptr_32);
10
11
    ptr_32  = (char *)malloc( 32);  // 0x20004680 <-- müsste 0x200045b0 sein
12
    ptr_128 = (char *)malloc(128);  
13
14
    free(ptr_128);
15
    free(ptr_32);
16
17
    ptr_128 = (char *)malloc(128); // 0x20004620 <-- müsste 0x200045b0 sein
18
    free(ptr_128);

malloc() funktioniert als solches zwar korrekt, aber ich versteh' nicht, 
wieso er, wo doch alles Speicher zurückerstattet worden war, nicht 
wieder von Anfang an Speicher vergiebt.
Der EWARM von IAR verhaelt sich übrigens so wie man es erwartet.

Hat jemand eine Erklaerung für dieses Verhalten?

von TriHexagon (Gast)


Lesenswert?

Es gibt keine Vorschrift wie sich malloc() allgemein zu verhalten hat. 
Das Verhalten wird von der Implementierung bestimmt und da gibt es 
nunmal mehrere Möglichkeiten dies zu Implementieren.

D.h. wenn du wissen willst, wie sich malloc bei jenem Kompiler verhält, 
musst du in die Dokumentation der C-Standard Bibliothek des Kompilers 
(oder Platform) nachsehen.

von g457 (Gast)


Lesenswert?

> ptr_32  = (char *)malloc( 32);  // 0x20004680 <-- müsste 0x200045b0 sein
                                                    ^^^^^^
Laut wem? Deinem Bauchgefühl? Oder Wunschvorstellung? Mir jedenfalls ist 
keine C-Runtime und auch kein C-Standard bekannt, der zusichern würde, 
dass man irgendwelche willkürlichen Annahmen über die Rückgaben von 
malloc machen darf (..die nicht spezifiziert sind ;-). Oder hast Du eine 
belastbare (relevante) Quelle, die anderes behauptet?

von Holger (Gast)


Lesenswert?

Das ist ja mal eine lustige Annahme ... und selbst wenn es so wäre, dass 
der Pointer für dieselbe, wiederholt angeforderte, Speichermenge 
jedesmal derselbe sein sollte, was hätte man davon? Spätestens im Falle 
mehrerer Threads könnte jederzeit ein anderer Thread Rechenzeit bekommen 
und zwischendurch genau diese Adresse per malloc für seine Anforderung 
wegschnappen... Das Programm, das malloc verwendet, darf sich also 
niemals darauf verlassen, dass die Adresse gleich ist.

Davon mal abgesehen ist die tatsächliche physikalische Speicheradresse 
im RAM-Baustein dank MMU im Prozessor wahrscheinlich sowieso jedesmal 
eine andere, selbst wenn die logische Adresse, die malloc liefert 
identisch sein sollte.

Ich unterstelle jetzt mal, dass sowas wie "ich nutze den Inhalt der eben 
gefree'ten Adresse einfach nochmal, indem ich nochmal diesen Speicher 
anfordere" gemacht werden soll ... das geht durch die MMU mit Sicherheit 
sowieso daneben...

Gruß Holger

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mehmet Kendi schrieb:
> nicht wieder von Anfang an Speicher vergiebt.

Vermutlich hast du ein malloc() dort, welches „eimerweise“ vergibt
und dann Anforderungen gleicher Größe vorzugsweise aus dem gleichen
„Eimer“ wieder bedient.  Dadurch bekommt deine erneute Anforderung
von 32 Byte wieder den Platz, den die zuvor bereits erteilte
32-Byte-Anforderung hatte.

Nachteil: man muss mehr Speicherreserven vorhalten.  Vorteil: das
Risiko einer Fragmentierung sinkt.

Um mal spaßeshalber noch ein völlig anderes System in die Runde zu
werfen, das hier passiert bei FreeBSD (Version 10.x, am64-Architektur):
1
ptr_128 = 0x801006080
2
ptr_64 = 0x801009040
3
ptr_32 = 0x80100a040
4
(freeing)
5
ptr_32 = 0x801006040
6
ptr_128 = 0x801009080
7
(freeing)
8
ptr_128 = 0x801006080

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Holger schrieb:
> Davon mal abgesehen ist die tatsächliche physikalische Speicheradresse
> im RAM-Baustein dank MMU im Prozessor wahrscheinlich sowieso jedesmal
> eine andere

Bei gcc-arm-none-eabi ist davon eher nicht auszugehen.

von Holger (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Holger schrieb:
>> Davon mal abgesehen ist die tatsächliche physikalische Speicheradresse
>> im RAM-Baustein dank MMU im Prozessor wahrscheinlich sowieso jedesmal
>> eine andere
>
> Bei gcc-arm-none-eabi ist davon eher nicht auszugehen.

Wenn man aber schon C schreibt, dann sollte das so aussehen, dass 
derartige Compilerabhängigkeiten keine Berücksichtigung finden. Mag ja 
sein, dass das bei diesem Compiler so ist ... schön, dass DU das weißt 
;) Festgelegt ist es aber nicht.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Mehmet Kendi schrieb:
1
>     ptr_32  = (char *)malloc( 32);  // 0x20004680
2
> 
3
>     free(ptr_128);
4
>     free(ptr_64);
5
>     free(ptr_32);
6
> 
7
>     ptr_32  = (char *)malloc( 32);  // 0x20004680 <-- müsste 0x200045b0 sein

Der malloc() auf Deinem PC (welcher Compiler/libc ist das) scheint ein 
FIFO-System zu verwenden, der malloc für Deinen µC eher ein LIFO.

Du kannst keine Annahmen darüber machen, wie und wo malloc() Speicher 
allokiert. Ich nehme mal an, dass der µC-malloc sogar eine bessere 
Methode verwendet: Wenn man annimmt, dass der nächste malloc() genauso 
viel Speicher braucht wie der letzte free()-Aufruf freigegeben hat, 
wirkt diese Methode einer Speicherfragmentierung entgegen.

von Klaus W. (mfgkw)


Lesenswert?

> Bei gcc-arm-none-eabi ist davon eher nicht auszugehen.



und selbst wenn, wäre das mapping auch relativ konstant, weil ja nicht 
jeder mit malloc() allokierte Block einzeln in physikalischen Speicher 
gemappt wird, sondern seitenweise ab der jeweils ersten Verwendung, 
unabhängig von malloc()- und free()-Folgen.

Natürlich, ohne daß man sich auf irgendwelche Reproduzierbarkeit 
verlassen dürfte...

: Bearbeitet durch User
von Mehmet K. (mkmk)


Lesenswert?

Wie malloc innerhalb des Programms mit seiner mir unbekannten Strategie 
Speicherplatz vergibt hat mich nie interessiert.
Aber wenn alles Speicherplatz zurückerstattet worden ist, haette ich - 
um  g457 (Gast) zu zitieren - aus dem Bauchgefühl heraus schon erwartet, 
dass malloc wieder ein "zurück zum Start" durchführt.

von Holger (Gast)


Lesenswert?

Mehmet Kendi schrieb:
> Aber wenn alles Speicherplatz zurückerstattet worden ist, haette ich -
> um  g457 (Gast) zu zitieren - aus dem Bauchgefühl heraus schon erwartet,
> dass malloc wieder ein "zurück zum Start" durchführt.

Wovon wir Dir mit gutem Bauchgefühl sagen können, dass Du Dich in dem 
Fall mal NICHT auf Dein Bauchgefühl verlassen kannst ;)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Mehmet Kendi schrieb:
> Aber wenn alles Speicherplatz zurückerstattet worden ist, haette ich -
> um  g457 (Gast) zu zitieren - aus dem Bauchgefühl heraus schon erwartet,
> dass malloc wieder ein "zurück zum Start" durchführt.

Es gibt nicht immer ein "zurück zum Start". malloc() unter unixoiden 
Systemen nutzt zum Beispiel sbrk()/brk() als Systemaufruf, um in 
größeren Happen Speicher vom System zu bekommen, als Du tatsächlich vom 
malloc() haben möchtest. Der einmal von malloc() benutzte Speicher wird 
bei free() im allgemeinen NICHT wieder ans System zurückgegeben, sondern 
bleibt für eine späterere Neu-Verwendung per malloc() weiterhin 
reserviert.

Von daher ist ein "zurück zum Start" ziemlich aussichtslos. Deine 
vorangegangen malloc()- und free()-Aufrufe haben das System (z.B. 
RAM-Reservierung) bereits geändert.

von Klaus R. (klausro)


Lesenswert?

Mehmet Kendi schrieb:
> gcc-arm-none-eabi

Verwendest du ein Embedded OS? Wenn ja, verwende (wenn unbedingt nötig) 
die malloc() und free() Funktionen des OS. Arbeitest du aber bare-metal, 
würde ich tunlichst auf malloc() verzichten. Statische 
Speicherverwaltung ist angesagt. Alles andere ist m. E. fahrlässig... 
man kann von MISRA halten was man will, aber an diesem Punkt haben sie 
m. E. recht.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Mehmet Kendi schrieb:
> Wie malloc innerhalb des Programms mit seiner mir unbekannten Strategie
> Speicherplatz vergibt hat mich nie interessiert.

Du kannst ja mal Deine

    free(ptr_128);
    free(ptr_64);
    free(ptr_32);

umdrehen, also:

    free(ptr_32);
    free(ptr_64);
    free(ptr_128);

Es könnte sein, dass der nächste malloc() dann anders vorgeht.

von Mehmet K. (mkmk)


Lesenswert?

Eine Umstellung der Free-Reihenfolge hat am Verhalten nicht geaendert.

Okay, danke für all die zahlreichen Antworten. Da meine Erwartungen 
bezüglich malloc nicht den Gegebenheiten zu entsprechen scheinen, 
brauche ich mir dieses Verhaltens wegen keine grauen Haare wachsen zu 
lassen.
Meine Befürchtung war gewesen, irgendwas sonst waere nicht ganz koscher.

von Mehmet K. (mkmk)


Lesenswert?

Klaus Rotter schrieb:
> Statische
> Speicherverwaltung ist angesagt. Alles andere ist m. E. fahrlässig...
> man kann von MISRA halten was man will, aber an diesem Punkt haben sie
> m. E. recht.

Klaus, sorry dass ich erst jetzt darauf eingehe; war aber nicht leicht, 
einen Beitrag aus dem Jaher 2008 wiederzufinden.
Beitrag "malloc/free vs. statische Variablen auf µC"

Das Ganze wurde zwar (wie in Foren so üblich) durch andere Querthemen 
etwas verwaessert, aber wenn man diese ausblendet: die eigentliche 
Diskussion zwischen Jörg und Yalu fand ich recht interessant.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.