Forum: Mikrocontroller und Digitale Elektronik malloc() auf uC


von Paul (Gast)


Lesenswert?

Mahlzeit,

oft wird einem ja von malloc() auf einem uC abgeraten.

Manchmal ist es aber doch ganz praktisch: z.B. ich habe irgendwelche 
Datensätze, die in Strukturen organisiert sind. Meinetwegen Adressen. 
Weil Straßennamen ja doch eine recht unterschiedliche Länge haben können 
macht es ja Sinn, anstatt eines Members „char adresse[MAX_LEN]“ einen 
Member „char *adresse“ anzulegen und den String separat anzulegen.
Eine Ähnliche Situation hab ich momentan, deswegen beschäftige ich mich 
mit malloc(), was ich bisher eher selten genutzt habe.

Aber wie funktioniert denn malloc() genau auf einem AVR?
Dort ist ja erstmal kein OS vorhanden, dass meinen Speicher verwaltet. 
Woher nimmt malloc() den Speicher, und woher weiß es wieviel noch frei 
ist? Wie gibt es Speicher wieder frei?

Würde doch gern bischen über die Internas Bescheid wissen, sonst ist mir 
das Ganze nicht geheuer :-)

Gruß, Paul

von Na Sowas (Gast)


Lesenswert?

Dazu braucht man auch kein OS, der Speichermanager, dh eine 
Libraryfunktion, uebernimmt die Verwaltung des unbenutzten Speichers. 
Ich wuerd's auch ohne dynamischen Speichermachen.

von Karl H. (kbuchegg)


Lesenswert?

Paul schrieb:

> Aber wie funktioniert denn malloc() genau auf einem AVR?
> Dort ist ja erstmal kein OS vorhanden, dass meinen Speicher verwaltet.

Und wie macht das OS das?

Mit deiner Argumentation verschiebst du das Problem ja nur. Nur weil du 
ein OS hast, löst sich ja der Problemkreis deswegen nicht in Luft auf 
:-)

> Woher nimmt malloc() den Speicher, und woher weiß es wieviel noch frei
> ist? Wie gibt es Speicher wieder frei?

Na zb, indem es den überhaupt freien Speicher erst mal als 
Speicherblöcke betrachtet über die Buch geführt wird.

Machst du eine Allokierung, dann wird ein möglichst gut passender Block 
gesucht. Passt der genau, dann kriegst du einen Pointer darauf und der 
Block kommt aus der 'Buchhaltung' raus. Passt der Block nicht genau, 
dann wird er in 2 Teile aufgeteilt, der eine, der genau passt, den 
kriegst du, der andere bleibt in der Buchhaltung.

Gibst du Speicher wieder frei, passiert genau das umgekehrte. Der Block 
geht wieder zurück in die Buchhaltung und es wird versucht, ob man 
eventuell 2 benachbarte kleinere Blöcke zu einem größeren verschmelzen 
kann.

Organisiert ist das ganze normalerweise als lineare Liste. D.h. in jedem 
Block steht die Adresse des jeweils nächsten Blocks und oft hält man 
diese Liste auch nach Adressen sortiert, weil man dann die Blockgröße 
durch Adressvergleich ermitteln kann und weil man beim free(), beim 
Verschmelzen, sowieso die benachbarten Blöcke suchen muss.

von Marwin (Gast)


Lesenswert?

Paul schrieb:
> Manchmal ist es aber doch ganz praktisch

Von malloc wird nicht abgeraten, weil es unpraktisch waere. Mach dir mal 
Gedanken ueber das Thema "Speicherfragmentierung" und was es fuer 
Systeme mit wenig Speicher bedeutet.

von Karl H. (kbuchegg)


Lesenswert?

> Eine Ähnliche Situation hab ich momentan

Tja. Das Problem ist, das du zwar rein rechnerisch noch 10k frei haben 
kannst und trotzdem keinen Speicher mit 64Bytes allokieren kannst, weil 
du nur lauter kleine Speicherstückchen frei hast, die zwar in Summe die 
10k ergeben, aber von denen keiner größer als sagen wir mal 32Bytes ist.

Je mehr Speicher du insgesamt hast, auch im Vergleich zu deinen 
Allokierungen, desto geringer wird diese Gefahr. Je weniger Speicher 
überhaupt verfügbar ist, desto größer wird die Gefahr.

Und da hat man auf einem µC lieber gesicherte Zahlen. Sag ich meinem 
Benutzer, du kannst 100 Adressen ins Adressbuch legen, wobei der 
Strassenname maximal 40 Zeichen umfassen darf, dann hat er was, mit dem 
er arbeiten kann. Ist aber nach 80 Adressen schon Schluss, weil ein 
Strassenname mit 120 Zeichen nicht mehr angelegt werden kann, dann wird 
er mir was husten.

von Paul (Gast)


Lesenswert?

Fragmentierung ist klar, die gibt es natürlich.

Das „Buchhaltungsprinzip“ kenn ich auch. Allerdings sind da doch 
bestimmt einige Einträge nötig pro angeforderten Datenblock. Sprich, ich 
habe einiges an Overhead, vor allem bei vielen kleinen Blöcken, richtig?

Und malloc() weiß ja zu Beginn auch noch nicht, wieviele Blöcke es 
später mal zu verwalten hat, sprich, malloc() müsste evtl. auch 
dynmischen Speicher anfordern?

von Karl H. (kbuchegg)


Lesenswert?

Paul schrieb:
> Fragmentierung ist klar, die gibt es natürlich.
>
> Das „Buchhaltungsprinzip“ kenn ich auch. Allerdings sind da doch
> bestimmt einige Einträge nötig pro angeforderten Datenblock.

Nicht wirklich.
Wenn du den Adresspointer kriegst, dann ist er raus aus der 
Speicherverwaltung. Der belegt in der Verwaltung nichts mehr, der 
Speicher gehört dir.

> Sprich, ich
> habe einiges an Overhead,

Innerhalb der Verwaltung hast du einen Pointer in jedem Block und eine 
globale Variable, die auf den ersten Block zeigt.

> Und malloc() weiß ja zu Beginn auch noch nicht, wieviele Blöcke es
> später mal zu verwalten hat, sprich, malloc() müsste evtl. auch
> dynmischen Speicher anfordern?

No. die Information darüber liegt sinnvollerweise in den Speicherblöcken 
selber. Ist ja nur ein Pointer auf den jeweils nächsten Block.

von Karl H. (kbuchegg)


Lesenswert?

Und auf einem µC kommt noch was dazu.

Du weißt am Anfang nicht, wieviel Speicher du für den Heap überhaupt 
hast. Heap und Stack müssen sich ja denselben Speicher teilen.
Der Stack wächst traditionell von oben nach unten. Wieviel Stack 
gebraucht wird hängt davon ab, wieviele und welche funktionslokale 
Variablen du hast, wie sich Funktionen gegenseitig aufrufen, was 
wiederrum des öfteren von Aussen gesteuert wir (Benutzereingaben, etc.).
Die Heap-Allokierungen finden von unten nach oben statt. Und irgendwann 
treffen sich die beiden, wenn man Pech hat.

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


Lesenswert?

Paul schrieb:
> Aber wie funktioniert denn malloc() genau auf einem AVR?

RTFDoc:

http://www.nongnu.org/avr-libc/user-manual/malloc.html

Karl Heinz Buchegger schrieb:
> Tja. Das Problem ist, das du zwar rein rechnerisch noch 10k frei haben
> kannst und trotzdem keinen Speicher mit 64Bytes allokieren kannst, weil
> du nur lauter kleine Speicherstückchen frei hast, die zwar in Summe die
> 10k ergeben, aber von denen keiner größer als sagen wir mal 32Bytes ist.

Ja, das kann passieren.

Allerdings ist es bei einem wirklich dynamischen Problem so, dass
man bei statischer Vergabe (mit Annahme entsprechender Maixmalwerte)
dann in der Regel viel weniger unterbringt, weil man den Speicher
deutlich schlechter ausnutzt, oder man muss mehr in einen größeren
Speicher investieren.  Du garantierst deinem Nutzer die 100 Adressen
mit maximal 40 Zeichen langen Straßennamen, aber obwohl der Speicher
dann am Ende nur zu 1/3 ausgenutzt ist, bekommt er partout keine
101. Adresse mehr unter.  Paul würde ihm bei gleichem Speicherausbau
"durchschnittlich 200 Adressen" anbieten können, sehr wahrscheinlich
bekommt der Nutzer sogar noch mehr unter, und auch ein einzelner
Straßenname mit 45 Zeichen ist kein Problem.

In jedem(*) Falle muss der Nutzer bzw. die Applikation jedoch mit der
Situation umgehen können, dass der Speicher einfach irgendwann voll
ist.  Dessen muss man sich von vornherein bewusst sein.

(*) Also keinesfalls nur in der malloc-Variante!

Das Problem der Speicherfragmentierung wird um so größer, je stärker
der Speicher dann an der Auslastungsgrenze arbeitet.  Wenn man ein
wirklich dynamisches Problem (mit sich häufig ändernden, variabel
langen Datenfeldern) hat und es ist noch "etwas Luft", dann korrigert
sich das statisch gesehen über die Lebensdauer einfach von selbst,
weil ja auch immer wieder was freigegeben wird.  Je größer die
Auslastung des Speichers wird, um so schwieriger wird es für den
Allokator dann, einen hinreichend großen passenden freien Block noch
zu finden, bis dann irgendwann "Pumpe" ist.

Karl Heinz Buchegger schrieb:
> Du weißt am Anfang nicht, wieviel Speicher du für den Heap überhaupt
> hast. Heap und Stack müssen sich ja denselben Speicher teilen.

Nun, das Problem hast du allerdings bei statischer Allozierung
genauso: du weißt am Anfang auch nicht, wieviel Stack du wirklich
brauchst und ob dein allozierter Datenbereich vielleicht wirklich
irgendwann mit dem Stack kollidieren wird.  Nun bist du entweder
übervorsichtig und planst für den Stack genügend Reserve ein (dann
sinkt wiederum die relative Speicherauslastung noch mehr), oder du
riskierst einen Komplettcrash wegen einer Kollision des Datenbereichs
mit dem Stack.  Mit malloc() (zumindest dem der avr-libc) ist diese
Situation sogar noch geringfügig besser in diesem einen Fall, da
vor dem Allozieren eines weiteren Bereichs in Richtung Stack
zumindest getestet wird, ob der Bereich (zuzüglich einer einstellbaren
Sicherheitsreserve) bereits mit dem aktuellen Stack kollidiert oder
nicht.

Alles in allem sind derartige Probleme in einer so stark eingeschränkten
Umgebung wie einem kleinen Controller immer mit irgendwelchen Problemen
behaftet.  Mit genügend Geld (=> mehr Speicher) kann man dabei die
Grenze immer in Richtung "sicherer" verschieben.

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.