Archiv verlassen und diese Seite im Standarddesign anzeigen : Dynamische Speicherverwaltung für AVR Sinnvoll?
crabtack
29.07.2015, 21:51
Nabend!
Ich bin gerade dabei, einen CanOpen Stack auf einem Atxmega zu implementieren.
Bisher funktioniert auch alles Wundervoll, ich kann SDOs schicken und empfangen, das gleiche gilt für PDOs und PDO Mapping klappt auch schon, selbst die SPS meckert nicht mehr bei der Kommunikation :D.
Jetzt habe ich momentan das Problem, dass der Stack möglichst Flexibel für verschiedene Anwendungen genutzt werden soll.
Das heißt es müssen alle nötigen Objekte im Speicher vorhanden sein.
Aktuell Lege ich jedes Objekt einzeln fest, in der Art:
CanObject LedState,Pulswidth,IOReg;
Das Problem ist, dass auf die Weise viele eventuell nicht genutzte Objekte den Speicher unnötig füllen.
Am besten wäre, wenn immer nur die Objekte erzeugt werden, die die SPS per SDO konfigurieren will (Natürlich mit einigen Ausnahmen).
Also benötige ich eine Möglichkeit, die Objekte dynamisch, zur Laufzeit zu erzeugen.
Würde ich für einen PC Programmieren würde ich zu einer map greifen.
Auf dem Controller halte ich das für zu ineffektiv.
Was ist von der Nutzung von malloc() zu halten?
Dazu habe ich von vielen schlechtes, von einigen aber auch gutes gehört.
Hat jemand schon Erfahrungen mit so etwas gemacht und kann mir weiter helfen?
Oder macht vielleicht ein externer Speicher mehr sinn?
Grüße
Olaf
Peter(TOO)
30.07.2015, 03:52
Hallo Olaf,
Was ist von der Nutzung von malloc() zu halten?
Dazu habe ich von vielen schlechtes, von einigen aber auch gutes gehört.
Hat jemand schon Erfahrungen mit so etwas gemacht und kann mir weiter helfen?
Das ist das selbe Problem wie mit Zeigern in C.
Genial, wenn man damit umgehen kann und frustrierend wenn man das Konzept nicht verstanden hat und nicht aufpasst!
Es gibt drei grundlegende Gefahren:
1. Wenn man mit malloc() zu wenig Platz für ein Objekt anfordert überschreibt man natürlich irgendein anderes Objekt. Wie sich die Software verhält hängt jetzt davon ab, welches Objekt sich gerade hinter diesem Objekt befindet. Wenn der Speicher unbenutzt ist, passiert im besten Fall scheinbar gar nichts. Da die Speicherverwaltung dynamisch ist, wird auch das Verhalten der Software entsprechend unvorhersehbar. Möglich ist auch, dass man den Heap killt, in den ersten Bytes eines Verwaltungsblocks befinden sich Verwaltungsinformationen.
2. Wenn man den Block mit free() zurückgegeben hat, kann man natürlich weiterhin mit dem Zeiger auf diesen Speicherbereich zugreifen. Auch jetzt ist das Verhalten nicht vorhersehbar. Je nachdem ob der Block schon weiter verwendet wird oder nicht. Aus Laufzeitgründen wird der Block mit free() auch nicht auf 0 gesetzt, die Daten des Objekts stehen also noch im Speicher.
Hier hilft es wenn man, zumindest bei der Debug-Version folgendermassen vorgeht:
free(ptr);
ptr = NULL;
Allerdings muss man auch alle Kopien von ptr zurücksetzen, wobei es schon grundsätzlich schlechter Programmierstil ist, hier mit mehreren Variablen zu arbeiten ....
3. Vergisst man abgelaufenen Objekte mit free() an den Heap zurückzugeben geht einem mit der Zeit der Speicher aus.
Oder macht vielleicht ein externer Speicher mehr sinn?
Den externen Speicher musst du dann für die maximale Objektgrösse und die maximale Anzahl an Objekten auslegen!
Wenn die typischen Objekte z.B. 16 Byte gross sind und das Grösste 4KB, versaust du jede Menge Speicher :-(
Für 10 Objekte brauchst du dann rund 40KB Speicher. Da könnte man aber problemlos über 2'500 16-Bit Objekte unterbringen.
Hat jemand schon Erfahrungen mit so etwas gemacht und kann mir weiter helfen?
Jede Menge, habe auch schon selber malloc() und Co selber implementiert, allerdings auf Renesas-CPUs (H8-Familie)
Im einfachsten Fall hat jedes Heap-Element einen Header mit einem Zeiger auf das nächste Element und einem Flag welches angibt ob der Block frei oder belegt ist.
Da die H8 16-Bit Zugriffe nur auf gerade Adressen machen können, belegt die Standardvariante, beim 16-Bit Speichermodell, für den Header 4 Bytes. 2Bytes für den Zeiger. 1 Bit für das Flag und 15 Bit sind verschwendet :-( Ich habe dann das Flag in das letzte Adressbit gepackt, die H8 setzen das letzte Bit bei einem Speicherzugriff einfach per Hardware auf 0. Bei kleinen Objekten und nur ein paar KB RAM macht das einiges aus.
MfG Peter(TOO)
Du musst bedenken, dass du bei AVRs keine Möglichkeit hast, einer Speicherfragmentierung entgegen zu wirken. Wenn du Objekte unterschiedlicher Größe hast und nur manche freigibst und andere nicht, kannst du dir den Heap so zerstückeln dass irgendwann größere Datenstrukturen nicht mehr als ganzes unterzubringen sind.
mfG
Markus
Peter(TOO)
31.07.2015, 12:57
Hallo Markus,
Du musst bedenken, dass du bei AVRs keine Möglichkeit hast, einer Speicherfragmentierung entgegen zu wirken. Wenn du Objekte unterschiedlicher Größe hast und nur manche freigibst und andere nicht, kannst du dir den Heap so zerstückeln dass irgendwann größere Datenstrukturen nicht mehr als ganzes unterzubringen sind.
Die Fragmentierung in dieser Form hat man aber nur unter bestimmten Bedingungen.
Vor allem hängt es davon ab, wie das Grössenverhältnis zwischen den Elementen und zum Heap ist.
Normalerweise ist dies kein Problem.
MfG Peter(TOO)
TheDarkRose
02.08.2015, 13:43
Man sollte natürlich auch beachten, dass der Heap (malloc/free/realloc) nur Code ist. Also brauchst du zuerst mal eine Implementierung, wie z.b die avr-libc. Dann solltest du beachten, das AVR keinen Speicherschutz haben. Der Stack fängt immer vom Ende des Speichers an. Der Heap wird dann nach .bss und .data Segmenten plaziert. D.h. Heap und Stack arbeiten aufeinander zu und können sich gegenseitig zerstören. Die Heapimplementierung kann zwar prüfen, dass es nicht in den Stack reinarbeitet. Aber da der Stack nichts vom Heap weiß, kann der Stack dir den Heap kaputt machen. Also du solltest unbedingt überdenken was in den Heap soll. Und auch auf die Rekursionstiefe vom Funktionen ist dann acht zu geben, dass der Stack nicht zu groß wird.
Gegen die Speicherfragmentierung des Heaps hilft natürlich eine möglichst intelligente malloc/free-Routine, die Fragmentierung vermeidet oder im Notfall sogar den Heap defragmentieren kann.
TheDarkRose
02.08.2015, 21:54
wenn deine Daten zwar dynamisch unterschiedlich groß sind, aber eigentlich relativ klein und nur innerhalb der einen Funktion leben müssen, wäre alloca() (http://www.nongnu.org/avr-libc/user-manual/group__alloca.html) auch eine Möglichkeit. Damit kannst du dynamisch Speicher am Stack allozieren, welcher beim Verlassen der Funktion automatisch wieder frei gegeben wird.
Gegen die Speicherfragmentierung des Heaps hilft natürlich eine möglichst intelligente malloc/free-Routine, die Fragmentierung vermeidet oder im Notfall sogar den Heap defragmentieren kann.
Den Heap defragmentieren geht nicht bei AVRs. Und die möglichst intelligente malloc/free-Routine gibt es auch nicht, weil immer von deinem Einsatzszenario abhängt, was möglichst intelligent ist. Wenn du malloc sparsam und unter Berücksichtigung der Arbeitsweise einsetzt, bekommst du mit einer Speicherfragmentierung keine Probleme. Unbedachtes malloc fliegt dir wahrscheinlich um die Ohren wenn du Objekte mit sehr unterschiedlicher Lebensdauer hast.
mfG
Markus
TheDarkRose
03.08.2015, 20:31
Gut, defragmentieren war etwas überspitzt. Dennoch, wenn zwei Blöcke nebeneinander freigegeben werden, sollte natürlich die Heapverwaltung erkennen, dass diese wieder zu einen zusammenhängenden freien Block werden. Wenn es nicht um Geschwindigkeit geht, kann man natürlich schon speichereffektivere malloc-Routinen verwenden.
Aber wie gesagt, wenn die Bedingung ist, dass einfach nur dynamisch die Größe des eigentlichen Objektes nötig ist, und der Pointer nicht raus aus der Methode muss (also den Pointer returnen), dann sollte alloca() die bessere Alternative sein.
Ich bin im Embedded Bereich (vor allem bei so kleinen Controllern) im allgemeinen gegen Malloc und Co und nutze sie nur im Notfall. Nur wenn aller genutzter Speicher statisch allokiert wird, kannst du dir sicher sein, dass dir der RAM nicht ausgeht. Am PC ist das nicht so wild, weil a) viel viel mehr RAM da ist und b) RAM auf die Festplatte ausgelagert werden kann, wenn der Platz knapp wird.
Wenn überhaupt würde ich für deinen CanOpen Stack einen festen Bereich reservieren und den je nach Bedarf neu zu teilen. Allerdings musst du dir auch hier sicher sein, dass 2 Komponenten nicht gleichzeitig den selben Speicher brauchen.
Powered by vBulletin® Version 4.2.5 Copyright ©2024 Adduco Digital e.K. und vBulletin Solutions, Inc. Alle Rechte vorbehalten.