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
Dazu braucht man auch kein OS, der Speichermanager, dh eine Libraryfunktion, uebernimmt die Verwaltung des unbenutzten Speichers. Ich wuerd's auch ohne dynamischen Speichermachen.
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.
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.
> 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.
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?
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.