Hallo zusmamen, kurze Frage aus persönlichem Interesse bzw. Unwissenheit. Ich programmiere die Texas instrumentss MSP430 Serie hoch und runter, jetzt bin ich nach Jahren mal drauf gestossen die Speicher näher zu betrachten. Bisher benutze ich wahrscheinlich mehr globale Variablen als für ein embedded projekt gut sind dazu habe ich natürlich noch einen stack der nach belieben Richtung globaler Variablen ( jetzt von der Speicheraddressierung her gesehen ) anwächst. Wenn es einen stack overflow gibt bekommt man das natürlich durch merkwürdiges Verhalten des uC mit, oder manchmal eben auch nicht :) Jetzt habe ich entdeckt dass ich auch meistens einen heap mit initialisiert habe, der wird dann im RAM auch bereitgehalten, wird aber in meiner Programmierung eigentlich nie benutzt. Falls man consolen-debugging betreibt manchmal, weil malloc eben in der printf-Funktion auftaucht, aber im allgemeinen ist es ja wieder egal ob mein stack in den heap bereich anwächst ( also "egal" weil ich den heap im Endprodukt nicht verwende ) Das ist jetzt nicht wirklich wünschenswert, daher häufen sich meine Fragen bezüglich heap an, und ich bekomme die gerade nicht mehr selber gelöst. Ich habe versucht einen Überblick über alte heap und memory allocation threads und blogs usw zu bekommen. Bisher würde ich sagen für uC, also nicht die STM32 ARM Flaggschiffe usw mit < 16 kB RAM, kommt es nicht wirklich in Frage ein malloc und damit den heap zu verwenden, weil die Programmierung ja sowieso von worst case Speicherszenarien ausgehen muss und daher ist es durchaus sinnvoll ( steht zur Diskussion ) alles static bzw global zu machen was kritisch ist (sei es speichertechnisch oder zeittechnisch ). Wie ist das das Allgemeine Bild, wer programmiert uCs mit kleinem RAM und verwendet den heap, oder seid ihr meiner MEinung, dass der heap dort einfach keine Rolle spielt, weil man ja alle Benutzungsszenarien genau weiss und daraufhin seinen Speicher eben plant und einsetzt? Vielen Dank fürs Lesen und eventuell für eure Meinungen. Viele Grüße.
mex schrieb: > Wie ist das das Allgemeine Bild, wer programmiert uCs mit kleinem RAM > und verwendet den heap, Ich nicht. > oder seid ihr meiner MEinung, dass der heap dort > einfach keine Rolle spielt, weil man ja alle Benutzungsszenarien genau > weiss und daraufhin seinen Speicher eben plant und einsetzt? Zu 99% ja. Es mag Ausnahmen geben. Aber auch dann stellt sich die Frage, ob eine statische Speicherreservierung mit "manueller" Verwaltung besser ist.
Heap ist auf Microcontrollern quatsch. Die Idee dahinter ist ja das verschiedene Teile eines Programs (Tasks) ihren Speicher nicht immer brauchen. Man kann dann also die knappe Resource Speicher besser nutzen. Aber wenn man so denkt dann muss man sich darueber im klaren sein das die Anforderungen von heap-Speicher auch mal fehlschlagen kann weil dieser Speicher von anderen Teilen gerade genutzt wird. Wie reagiert man dann auf einem Mikrocontroller? Auf die Festplatte swappen? Fenster aufmachen und sich beim Anwender beschweren? Steuerung solange anhalten? Olaf
Olaf schrieb: > Wie reagiert man > dann auf einem Mikrocontroller? Auf die Festplatte swappen? Fenster > aufmachen und sich beim Anwender beschweren? Steuerung solange anhalten? Auf µCs gibt es eigentlich nur eine Antwort: Resetten! SCNR.
Wir verzichten auch auf den Heap, Allerdings benötigen wir für die cryptography trotzdem eine dynamische Speicherallokierung. Mittlerweile habe ich alleine in unserer Firma 5 verschiedene Memorypool implementierungen gesehen -.-" <ironie>warum wiederverwenden wenn man das Rad neu erfinden kann?</ironie>
Ich kenne mich ja mit dem MSP nicht aus, aber soweit mir bekannt werden primär lokale Variablen auf dem Heap abgelegt. Weiterhin landen hier die Rücksprungadressen der Unterfunktionen und die "automatisch" gesicherten Register, wenn sich eine Funktion im Prozessor richtig breit macht. In wie fern die Laufzeitumgebung ihren "Eigenbedarf" dorthin verfrachtet, weiß ich nicht.
:
Bearbeitet durch User
Roland D. schrieb: > Mittlerweile habe ich alleine in unserer Firma 5 verschiedene Memorypool > implementierungen gesehen -.-" https://de.wikipedia.org/wiki/Not-invented-here-Syndrom
Sebastian S. schrieb: > Ich kenne mich ja mit dem MSP nicht aus, aber soweit mir bekannt werden > primär lokale Variablen auf dem Heap abgelegt. > > Weiterhin landen hier die Rücksprungadressen der Unterfunktionen und die > "automatisch" gesicherten Register, wenn sich eine Funktion im Prozessor > richtig breit macht. Ja also für den MSP430 mit CodeComposerStudio ist das sicherlich falsch, ich denke dass ist im Allgemeinen für uCs falsch, aber da wissen sicher andere mehr. Ich kann den kompletten Speicher, also Flash sowhol als RAM im Code Composer nachvollziehen während der Laufzeit und dort landet nie etwas auf dem Heap, insofern dieser nicht genutzt wird. Sobald befehle wie malloc oder funktionen wie printf angewendet werden, wird dieser sehrwohl gefüllt. Zumindest auf meinem System, wie ich vermute auch auf denen aller hier Schreibenden, sind eben alle Variablen entweder globale, die haben dann sowieso eine feste Zuordnung innerhalb des RAMs, oder die lokalen Variablen in Unterfunktionen etc. werden spontan auf dem Stack erstellt und dort eben auch wieder überschrieben, sobald die Funktion verlassen wurde.
Sebastian S. schrieb: > Ich kenne mich ja mit dem MSP nicht aus, In der Tat. > aber soweit mir bekannt werden > primär lokale Variablen auf dem Heap abgelegt. Nö, die landen auf dem Stack. > Weiterhin landen hier die Rücksprungadressen der Unterfunktionen und die > "automatisch" gesicherten Register, wenn sich eine Funktion im Prozessor > richtig breit macht. Auch Stack. > In wie fern die Laufzeitumgebung ihren "Eigenbedarf" dorthin > verfrachtet, weiß ich nicht. "Wenn man keine Ahnung hat, einfach mal Fresse halten."
(PSOC3/PSOC5lp) Heap benutze ich selber nicht aber es gibt libraries die ich benutze die ein minimalen HEAP erfordern
Olaf schrieb: > Wie reagiert man dann auf einem Mikrocontroller? Genauso, wie man reagieren würde, wenn man für ein dynamisches Problem die viel gepriesene (siehe Falk weiter oben) statische Allozierung benutzt und der präallozierte Speicher am Ende ist. Soll heißen: wenn man ein dynamisches Problem hat (nur dann muss man über malloc & Co. überhaupt nachdenken), dann muss man sich um diesen Aspekt sowieso Gedanken machen. Statisches Präallozieren oder gar ein eigener dynamischer Allokator ist dann in aller Regel die schlechtere Wahl. Beim Präallozieren verschwendet man massiv Speicher, falls die Größe der dynamischen Objekte stark schwankend und nicht vorhersagbar ist, sodass man am Ende viel schneller Gefahr läuft, out of memory zu geraten (und dann obige "Reißleine" ziehen muss). Beim "selbstgestrickten" riskiert man neue, eigene Bugs, die man erstmal rausfinden und beseitigen muss. Von der Gesamtmenge des verfügbaren RAMs hängt das übrigens alles gar nicht weiter ab. Es hängt nur davon ab, ob man solch ein Problem überhaupt lösen muss. Ich habe durchaus auch auf AVRs schon dynamischen Speicher benutzt – sofern es eben notwendig war.
:
Bearbeitet durch Moderator
Was wäre zum Beispiel ein Fall für heap Benutzung im uC? Ich stelle mir das so vor: Meine while(1) Schleife beinhaltet mehrere Unterfunktionen in denen ich nun verschiedenes zu tun habe, ich würde in jeder Unterfunktion gerne zB 12 kB RAM unterschiedlich benutzen, zum Beispiel einmal ein Datenlogging mit Mittelwertbildung oder so. Im anderen Unterpunkt will ich ein Vielschichtiges Menu auf den angeschlossenen LCD bringen und benötige dazu zB auch wieder 6 kB RAM, insgesamt stehen mit aber nur 16 kB RAM zur Verfügung. Also jetzt würde es gehen jeweils den heap zu benutzen und speicher eben neu zuzuordnen, richtig? Oder einen workaround, dass ich eben globale arrays doppelbenutze, mit der Gefahr dass die Nomenklatur durcheinander kommt weil eben einmal Mittelwert[1024] wirklich der Mittelwert ist und einmal eine LCD_screen array. Könnte man das so beschrieben für die Praktiker unter uns? Auch ein Dankeschön an alle die hier weiterhin mitlesen und schreiben :)
mex schrieb: > Was wäre zum Beispiel ein Fall für heap Benutzung im uC? Ein Beispiel wäre es, wenn du von außen Nachrichten bekommst, bei denen du vorher nicht weißt, wann sie eintreffen und wie groß sie im Einzelfall sind. Du musst sie dann verarbeiten und das Ergebnis wieder ausgeben. Dein Szenario lässt sich auch mit lokalen Variablen erschlagen. Allerdings haben diese (anders als der Heap) gleich gar keinen Check, dass der Platz auch ausreichend vorhanden ist.
:
Bearbeitet durch Moderator
mex schrieb: > Also jetzt würde es gehen jeweils den heap zu benutzen und speicher eben > neu zuzuordnen, richtig? Nö. Lokale Variablen kannst Du einfach auf dem Stack ablegen und sie werden automatisch beim Verlassen der Funktion wieder freigegeben. Malloc braucht man nur, wenn eine Task Daten erzeugen soll, die aber erst von einer anderen Task wieder freigegeben werden, d.h. den Erzeuger überleben müssen. Zugegeben, solche Anforderungen hat man auf MCs recht selten. Oft reicht einfach eine kleine FIFO, um zeitweise hohe Datenmengen über UART oder CAN zu puffern, bevor man sie auswertet. Interfaces arbeiten oft mit einem Protokoll, d.h. nach einer bestimmten Datenmenge wartet der Sender, bis der Empfänger sie quittiert hat.
Arduino benutzt den Heap auf Mikrocontrollern mit wenig Speicher. Das gängigste Modell ist der ATmega328 mit 2kB RAM. Ich denke der offensichtlichste Anwendungsfall bei Arduino ist die String Klasse: https://www.arduino.cc/reference/en/language/variables/data-types/string/ Die Klasse ist ist geradezu prädestiniert, Speicherüberläufe und Heap-Fragmentierung zu verursachen, wenn man nicht ganz genau weiß, was man da tut. Eigentlich sollte Arduino die Programmierung vereinfachen, aber mit dieser Klasse ist es nur scheinbar einfacher, in Wahrheit jedoch viel komplexer.
Stefanus F. schrieb: > Die Klasse ist ist geradezu prädestiniert, Speicherüberläufe und > Heap-Fragmentierung zu verursachen, wenn man nicht ganz genau weiß, was > man da tut. Und, wie viele Berichte über deswegen abstürzende Arduinos gibt es so im Netz? Mir fällt gerade kein einziger ein … dieses ständige Gespenst von der Speicherfragmentierung, was immer wieder so gern an die Wand gemalt wird, finde ich allmählich recht albern. Übrigens: Speicherüberläufe werden normalerweise durch den guard space zwischen Stack und Heap abgefangen: malloc() gibt dann 0 zurück. Allerdings, wie oben schon diskutiert, muss die Anwendung natürlich mit dieser Situation zurecht kommen. Wie ebenfalls schon erwähnt, lokale Variablen genießen einen derartigen guard space nicht. Die rennen hoffnungslose in das obere Ende des statischen Datensegments hinein, wenn sie zu groß / zu viele werden!
:
Bearbeitet durch Moderator
Jörg W. schrieb: > Und, wie viele Berichte über deswegen abstürzende Arduinos gibt es so im > Netz? Ich habe nicht gezählt, aber es sind viele. Insbesondere in Kombination mit Kommunikationsschnittstellen.
Der Heap wird bei mir grundsätzlich und in so gut wie jeder Anwendung benutzt, weil ich das printf der libc benutze. Funktional ist er also immer. Statische Allokation ist oft ausreichend und meine bevorzugte Variante. Wenn das nicht geht, dann lässt sich die Größe oft während der Systeminitialisierung ermitteln (z.B. anhand von Konfigurationsdaten) und dynamisch alloziieren. Beide Varianten sind für mich äquivalent. Dynamische Probleme erfordern dynamische Lösungen, aber die hatte ich auf wirklich kleinen Controllern noch nicht. Das oben genannte Kommunikationsprotokoll ist ein Beispiel. Oft gibt es aber genug Randbedingungen, die das nicht erzwingen (z.B. reicht oft ein Eingangspuffer maximaler Nachrichtenlänge, der dann zügig zerparst wird).
S. R. schrieb: > Der Heap wird bei mir grundsätzlich und in so gut wie jeder Anwendung > benutzt, weil ich das printf der libc benutze. Funktional ist er also > immer. Beim AVR braucht printf nur dann malloc(), wenn man die Gleitkomma-Version benutzen möchte. Das „normale“ printf kommt mit einem statisch allozierten Puffer aus. Das ist aber eine eigens für den AVR „herunter getrimmte“ stdio-Implementierung. Newlib/ARM hält sich dagegen mit seinem stdio an übliche Vorlagen aus anderen Bereichen (BSD etc.), die halt schon immer malloc() in stdio benutzt haben.
Ich benutze den Heap. Meine Steuerung muss unterschiedliche Hardware bedienen. Die Kontroll-Objekte werden dann dynamisch erzeugt, wenn klar ist, welche Komponenten angeschlossen sind. Bei mir sind das C++ Objekte, die dann mit new erzeugt werden. Das passiert allerdings nur einmal beim Hochfahren der Hardware. Während des Betriebes wird dann nichts mehr dynamisch alloziert oder freigegeben.
> Ich benutze den Heap. > Meine Steuerung muss unterschiedliche Hardware bedienen. [..] > Das passiert allerdings nur einmal beim Hochfahren der Hardware. Das ist jetzt das erste mal das ich denke, jo das macht Heap auf Controllern durchaus Sinn und schadet auch nicht. :) Was printf angeht, ich benutzt seit 20Jahren meine eigene Version. Die braucht keinen Heap, kann manches nicht was ein fettes Standard-printf kann, kann dafuer aber manches was ein normales printf nicht kann. (z.B Positionierung auf LCD, Integer als Festkomma ausgeben) Olaf
Jörg W. schrieb: > Beim AVR braucht printf nur dann malloc(), wenn man die > Gleitkomma-Version benutzen möchte. Für sprintf gilt das aber nicht? Ich finde jedenfalls kein malloc im Map-File.
Peter D. schrieb: > Jörg W. schrieb: >> Beim AVR braucht printf nur dann malloc(), wenn man die >> Gleitkomma-Version benutzen möchte. > > Für sprintf gilt das aber nicht? > Ich finde jedenfalls kein malloc im Map-File. Intern gehen alle auf vfprintf() zurück, auch sprintf(). Ich sehe aber gerade, dass bei Dmitrys Umbau vor Jahren auch bei Gleitkomma mittlerweile kein malloc() mehr nötig geworden ist.
Jörg W. schrieb: > Ich sehe aber gerade, dass bei Dmitrys Umbau vor Jahren auch bei > Gleitkomma mittlerweile kein malloc() mehr nötig geworden ist. Es geht auch ohne malloc(), wenn man einen passend großen, lokalen Puffer für den unformatieren String auf dem Stack reserviert.
m.n. schrieb: > Es geht auch ohne malloc(), wenn man einen passend großen, lokalen > Puffer für den unformatieren String auf dem Stack reserviert. Womit wir dann wieder dabei sind, dass lokale Variablen auf dem Stack komplett ungeprüft angelegt werden, im Gegensatz zu malloc(), das vorher auf ausreichend Platz wenigstens rudimentär testet …
Jörg W. schrieb: > mex schrieb: >> Was wäre zum Beispiel ein Fall für heap Benutzung im uC? > > Ein Beispiel wäre es, wenn du von außen Nachrichten bekommst, bei denen > du vorher nicht weißt, wann sie eintreffen und wie groß sie im > Einzelfall sind. Du musst sie dann verarbeiten und das Ergebnis wieder > ausgeben. Genau das haben wir ja auf einem Atmega664p damals zusammen gemacht.. > > Dein Szenario lässt sich auch mit lokalen Variablen erschlagen. > Allerdings haben diese (anders als der Heap) gleich gar keinen Check, > dass der Platz auch ausreichend vorhanden ist. Der Platz im RAM hätte bei rein statischer Allokation in dem Projekt damals gar nicht ausgereicht, deswegen habe ich ja über eine verkettete Liste den dynamisch allozierten Speicher verwaltet..und es hat funktioniert. Danke nochmal für Dein Debugging der malloc() Geschichte. Gruß, Holm
Stefanus F. schrieb: > Eigentlich sollte Arduino die Programmierung vereinfachen, > aber mit dieser Klasse ist es nur scheinbar einfacher, in Wahrheit > jedoch viel komplexer. Stefanus F. schrieb: > Ich habe nicht gezählt, aber es sind viele. Insbesondere in Kombination > mit Kommunikationsschnittstellen. Ich betrachte das als Arduino Bashing! Zumindest als Konstruktion und Verbreitung von Vorurteilen. (dass du dieses blinde Gehacke nicht irgendwann mal leid wirst...) Erfahrungsgemäß, und ich habe recht viel mit Arduino Usern zu tun, ist die C-String Verarbeitung, wie auch der sonstige Umgang mit Arraygrenzen, der größere Quell von Sorgen, für Anfänger. Alles lernbar. Etwas Wissen und etwas Übung, dann klappt das schon. Wie auch der Umgang mit Brotmessern. --- Zum Thema: malloc() und seine Brüder kommen bei mir selten direkt zum Einsatz. Eher new und delete. Aber auch diese nur recht sparsam.
Holm T. schrieb: >> Ein Beispiel wäre es, wenn du von außen Nachrichten bekommst, bei denen >> du vorher nicht weißt, wann sie eintreffen und wie groß sie im >> Einzelfall sind. Du musst sie dann verarbeiten und das Ergebnis wieder >> ausgeben. > > Genau das haben wir ja auf einem Atmega664p damals zusammen gemacht.. Das ist ja letztlich auch das Paradebeispiel, wo man sowas gebrauchen kann – und es hat auf diese Weise geholfen, die malloc-Implementierung der avr-libc auf Produktionsniveau zu bringen. Wer keine dynamisch anfallenden Daten hat, braucht sich auch keine Rübe um malloc() machen, aber wer sowas hat, sollte sich reichlich überlegen, ob er wirklich lieber seine eigenen Bugs und Limitierungen stattdessen neu erfinden möchte.
So, jetzt geb ich auch meinen Senf dazu ... Allerdings mit µCs mit 64 k und 128 k RAM. malloc ist bei vielen µC-Programmierern ein Schimpfwort. Lieber machen sie irgendwelche Klimmzüge, nur um malloc zu vermeiden. Ihre Argumente: * Dangling pointers, out of bounds access. -> Ja mei, gehört zum Handwerkszeug das ordentlich hinzubekommen.Ist ja auch nicht so schwierig. Kann man dokumentieren ob der Absender oder der Empfänger fürs free verantwortlich ist. * heap fragmentation. -> OK, ist mistig. Zugegeben. Kommt aber auf zwei Dinge an. Wie ist der heap implementiert. Es gibt verschiedene Methoden die unterschiedlich zu Fragmentierung neigen, abhängig davon was man macht. Wenn man nur für kurze Zeit allokiert (nur zur Werteübergabe an einen anderen Task), dann passiert da nicht viel. alloc und gleich ein free, kein Problem. * "Es geht immer ohne alloc" -> Nein, hängt von der Aufgabe ab. Bei mir sind es etwa 20 tasks die je nach Konfiguration nie verwendet werden. Wozu sollen die einen statischen Speicher zugewiesen bekommen? Dann gibt es so Speicherfresser wie TCP/IP (40 k) und TLS 1.2 (20 k). Die werden einfach ganz zu Anfang vorsichtshalber allokiert und dann, wenn sie doch nicht verwendet werden ge-free-t. Problematisch wird es, wenn man große Blöcke allokieren muss, aber leider nichts mehr frei ist. Muss man halt drauf reagieren (je nach SW-Architektur kann man einfach etwas warten). Und es ist leider so, dass man Probleme bekommt, wenn man mit den allocs knapp unter der Decke kratzt. Man braucht etwas Luft (bei mir sind es ca. 40 k beim 128 k µC) Wenn die allocs eher statischer Natur sind braucht man wenig Luft, wenn sie dynamisch sind mehr Luft. Bei mir sind es grob 1 Million allocs und frees pro Minute. Puffer werden dynamisch angepasst (mit realloc) und laufen tw. durch eine eigene Lib für dynamische Arrays. Die lib versucht die reallocs niedrig zu halten und macht auch tw. komplette frees. Also: Kommt drauf an, ist kein Teufelszeug und **kann** perfekt funktionieren. Ist aber auch kein Allheilmittel. Nachdenken hilft! Gruß, Nick
nickm schrieb: > Allerdings mit µCs mit 64 k und 128 k RAM. Was leicht am Thema vorbei ist, denn die Überschrift fragt ausdrücklich nach Umgebungen mit weniger als 16 KiB RAM (wenngleich ich deinen restlichen Ausführungen zustimme).
:
Bearbeitet durch Moderator
Heap macht erst ab einer gewissen Komplexität Sinn. ZB durch sämtliche Versionen eines rtOS oder fertige Implementationen von TCP oder so. Bei Controllern unter 16k RAM wird die Komplexität aber eher selten erreicht. Da kommt man mit ner main() und statischen Variablen sowie Stack meist noch hin. Bei so kleinen Kernen baut man sich auch für printf() meist eine zweckangepasste Alternative. Schon um nicht so viel Krempel einzukompilieren.
Roland E. schrieb: > Bei so kleinen Kernen baut man sich auch für printf() meist eine > zweckangepasste Alternative. Naja, < 16 KiB RAM ist ja nun nicht so klein, Flash hat man bei den allermeisten AVRs dann allemal genug, als dass man das printf-Fahrrad nicht zum 135. Mal selbst erfinden müsste.
Hmm ...
Die Stackgröße wird doch beim Linken zugewiesen. Somit ist auch klar,
wie groß der Heap ist (minus den statisch allokierten Speicher). Da gibt
es also keine Überraschungen.
Und wer sich nicht klar über den tatsächlich benötigten Heap ist,
verwende lint (oder PClint), dann weiß er es. lint schadet sowieso
nicht, ist immer wieder so ernüchternd. :-)
> Was leicht am Thema vorbei ist, ...
Darum hab ich gleich zu Anfang darauf hingewiesen.
Ich bin leider davon ausgegangen, dass das was ich weiter geschrieben
hab von den Methoden und den Problemen/Nichtproblemen für halbwegs
Intelligente umsetzbar ist.
Sorry! Ist natürlich weitaus erhellender sich über ein deppertes printf
ad nauseam zu unterhalten.
Gruß,
Nick
nickm schrieb: > Die Stackgröße wird doch beim Linken zugewiesen. Bei manchen Compiler-Umgebungen ist das so (oder bei einem RTOS), ansonsten ist die Optimalvariante, dass man allen nach der statischen Allozierung übrig gebliebenen RAM von der einen Seite als Stack und gegenläufig von der anderen Seite als Heap benutzen kann.
Da hat sich ja ganz schön was entwickelt. Danke an all die sinnbringenden und guten Beiträge, übrigens auch die die am Thema leicht vorbeigehen, das ist alles super mal zu lesen für was so der heap dann doch Sinn ergeben kann. Das mit unterschiedlichen Konfigs die am Anfang vom Programm eben utnerschiedlich zugewiesen werden, schien mir spontan sehr passend, also viele Möglichkeiten und am Anfang entscheidet man sich quasi was für ein PRogramm jetzt auf dem uC bis zum nächsten reset laufen sollte. Gerade den letzte Beitrag so empfinde ich das mitlerweile auch: alles global udn static festlegen, der restliche Speicherbereich sit dann entweder heap und stack, gegenläufig aufeinander zulaufend. Oder eben nur noch stack, was anderes kann ich mit dem freien RAM im Programmablauf ja sowieso nicht mehr anstellen wenn ich keinen heap verwenden sollte. Und noch ein paar konkrete Antworten/ Hinweise: MSP430 mit CodeComposerStudio benutzt im printf den malloc im sprintf keinen malloc also keinen heap. Ich habe übrigens ähnlcih verworren im Texas e2e Forum so etwas ähnliches erfragen wollen, dort melden sich aber irgendwie keinerlei Menschen, schön zu sehen dass hier durchaus rege Beiträge entstehen, während International anscheinend nur noch Leute nach Komplettlösungen suchen und keinerlei Wissenstransfer mehr in den Foren stattfindet. Also Danke weiterhin und viele Grüße.
> der restliche Speicherbereich sit > dann entweder heap und stack, gegenläufig aufeinander zulaufend. Ob das so toll ist, ist zumindest fraglich! Denn jetzt hat man zwei mögliche Fehler. Der heap geht aus, malloc liefert NULL. Da kann man drauf reagieren. Der stack geht aus und ... es kracht. Toll! Wenn man den Stackbedarf vorher ermittelt kann Fehler 2 nicht mehr passieren. Ausser man unterschätzt Rekursion. :-) Den Stackbedarf kann man klein halten, wenn man alles "flach klopft" Die einzelnen Funktionen werden als Tasks ausgeführt (state machines) und kommunizieren nur über Nachrichten. Was ist denn das für eine Umgebung bei der man den Stack nicht angeben muss/kann? Damit ich mich davon fernhalten kann. Gruß, Nick
nickm schrieb: > Wenn man den Stackbedarf vorher ermittelt kann Fehler 2 nicht mehr > passieren. Ah ja. Seit wann interessieren sich dekrementierende Stackpointer für deine zuvor vorgenommene Schätzung? (Wir reden hier ja von einfachen Controllern, also ohne MMU oder dergleichen.) Die dekrementieren einfach weiter, und pfeifen auf deine Schätzung. Den Stack auf "top of RAM" beginnen zu lassen, gibt halt immer noch ein Maximum an Stack – Schätzung hin, Schätzung her.
> Seit wann interessieren sich dekrementierende Stackpointer für deine > zuvor vorgenommene Schätzung? Wo hab ich bitte was von Schätzung geschrieben? Wenn für dich "ermitteln" Synonym für "schätzen" ist, will ich nicht wissen was ... ach, ich will es einfach nicht wissen. Ausser du machst es so: Wenn ich den Speicher allokiere, dann brauch ich sowieso grad wenig heap. Bis ich mehr heap brauch ist der Speicher sicherlich freigegeben. Erinnert mich eher an das Berufsbild Möbelpacker. Gruß, Nick
nickm schrieb: > ach, ich will es einfach nicht wissen. Ist auch gut so. Du willst keine Umgebung benutzen, in der man die Größe des Stacks nicht vorgeben kann, und ich will keine Umgebung nutzen, in der man die Größe des Stacks zur Compilezeit künstlich kastrieren muss.
> Wenn für dich "ermitteln" Synonym für "schätzen" ist, will ich nicht
Also, nochmal: wo ist jetzt die Antwort auf meine Frage?
Wo hab ich geschrieben "geschätzt"
Zusatzfrage:
Wo ist jetzt genau der Nachteil wenn ich den exakten Stackbedarf kenne
und den dann angebe ggü. der "Methode" Wird schon passen, regelt sich
von alleine. Notfalls halt mit Absturz.
Gruß,
Nick
nickm schrieb: > Wird schon passen, regelt sich von alleine. Diese "Methode" hast du lediglich erfunden. Aber macht nichts, du willst mich nicht verstehen, und ich zweifle für eine hinreichend komplexe Applikation an, dass der exakte Stackbedarf überhaupt vorab ermittelbar ist. Wir leben also offenbar beide in völlig verschiedenen Welten.
> und ich zweifle für eine hinreichend komplexe Applikation an, dass der > exakte Stackbedarf überhaupt vorab ermittelbar ist. Dann ist es jetzt ja klar. Du glaubst das geht nicht, also geht es nicht. Aber macht nix, ich ordne dich dann halt bzgl. dem Thema als Bastler ein. Ist ja in Ordnung. Zumindest für mich. PS: Ich hab mich jetzt mit meinem uralten Namen angemeldet. Was in letzter Zeit mit "nickm" gepostet wurde, bin ich. Kannst dir ja überlegen, deinen dümmlichen Beissreflex zu unterdrücken. Mach mir aber dennoch wenig Hoffnung, denn auf Fragen kannst du ja auch nicht antworten. Dennoch: Gruß, Nick
Nick M. schrieb: > Aber macht nix, ich ordne dich dann halt bzgl. dem Thema als Bastler > ein. Mach ruhig, das beeindruckt mich nicht weiter. ;-) Ist mir auch völlig egal, ob du hier angemeldet oder unangemeldet postest – davon hängt meine fachliche Einschätzung deiner Statements in keiner Weise ab. Wir haben halt zwei völlig verschiedene Welten, in denen wir leben.
> Mach ruhig, das beeindruckt mich nicht weiter. ;-)
Muss es auch nicht, habs nur der Ordnung halber gemacht.
Aber meine Fragen beantwortest du penetrant weiterhin nicht, oder willst
du nur verhindern, dass man zwischen deinem tatsächlichen Wissen und
deinem vorgegebenen eine gewisse Beziehung herstellen kann?
Also, nochmal:
* Wo hab ich geschrieben, dass ich den Stackbedarf abschätze?
* Welche Entwicklungsplattform lässt es nicht zu, den Stack festzulegen.
* Was ist gut daran, wenn der Stack und Heap unkontrolliert aufeinander
zuwachsen und sich gegenseitig überschreiben können. Im Gegensatz zu
Heap und Stack klar definiert und festgelegt. Wir reden hier von µC,
nicht von PCs. Nicht dass da noch irgendwelche blablubb-Ausflüchte
rauskommen.
Gruß,
Nick
Nick M. schrieb: > Also, nochmal: > * Wo hab ich geschrieben, dass ich den Stackbedarf abschätze? Meine Erfahrung besagt, dass es für eine hinreichend komplexe Applikation nicht möglich ist, einen Stackbedarf exakt festzulegen. Wenn in der IT immer alles so schön komplett vorab überschau- und vorhersagbar wäre, hätten wir keine Bugs, über die man immer wieder mal stolpert. Bei sehr kleinen embedded applications kann das sicher noch ganz gut gelingen, den Überblick über den kompletten Stack die ganze Zeit über zu haben (und bei sicherheitsrelevanten Applikationen muss man sie dann halt so klein halten). Aber „embedded“ ist ein weites Feld, wir haben mittlerweile mehr als das 10fache an Speicher im Vergleich zu den ersten Unixen (PDP11) in einem Mikrocontroller. Wenn man sowas auch wirklich auslasten möchte, ist es mit einer 100%igen Überschaubarkeit irgendwann vorbei. Wenn man den Stackbedarf wiederum wirklich exakt bestimmen kann, ist es am Ende auch kein Problem, wenn die Umgebung mehr Platz als den ermittelten Wert für den Stack bereitstellt. > * Welche Entwicklungsplattform lässt es nicht zu, den Stack festzulegen. Es geht darum, dass man bei manchen Plattformen den Stack vorab einschränken muss, so beispielsweise bei den von Atmel gelieferten ARM-Linkerscripts. Die gleiche Toolchain (ARM-GCC) kann man aber genauso gut so benutzen, dass man den Stack von vornherein auf dem maximal verfügbaren RAM setzt – auch das ist durchaus ein „festgelegter“ Stack, nur eben ein größerer. > * Was ist gut daran, wenn der Stack und Heap unkontrolliert aufeinander > zuwachsen und sich gegenseitig überschreiben können. Im Gegensatz zu > Heap und Stack klar definiert und festgelegt. Ein überlaufender Stack wird immer irgendwas überschreiben: ob er nun den Heap überschreibt oder die globalen Daten, bleibt sich doch dabei völlig gleich. Du kannst den Stackpointer (wenn es keine MMU gibt) ja doch nicht daran hindern, über den von dir vorab ermittelten Bereich hinaus zu wachsen. Die einzige Stelle, bei der man vor der Allozierung noch testen kann, ob es passt, ist der Heap – egal, ob die Grenze dafür nun vorab fest vorgegeben worden ist, oder ob der Test gegen das (untere) Ende des Stacks erfolgt.
:
Bearbeitet durch Moderator
nickm schrieb: [..] > > Was ist denn das für eine Umgebung bei der man den Stack nicht angeben > muss/kann? Damit ich mich davon fernhalten kann. > > Gruß, > Nick Intel 8008, Stack in Hardware nicht erweiterbar..8 Bytes. Irgendwie hatte es die Deutsche Reichsbahn auf diesem System geschafft ein Fahrkartenbuchungssystem zu implementieren das alle Bahnhöfe der DDR enthielt (ich denke das waren mehr als heute in D überhaupt existieren). Gruß, Holm
Nick M. schrieb: >> Mach ruhig, das beeindruckt mich nicht weiter. ;-) > > Muss es auch nicht, habs nur der Ordnung halber gemacht. > > > Aber meine Fragen beantwortest du penetrant weiterhin nicht, oder willst > du nur verhindern, dass man zwischen deinem tatsächlichen Wissen und > deinem vorgegebenen eine gewisse Beziehung herstellen kann? > > Also, nochmal: > * Wo hab ich geschrieben, dass ich den Stackbedarf abschätze? > * Welche Entwicklungsplattform lässt es nicht zu, den Stack festzulegen. > * Was ist gut daran, wenn der Stack und Heap unkontrolliert aufeinander > zuwachsen und sich gegenseitig überschreiben können. Im Gegensatz zu > Heap und Stack klar definiert und festgelegt. Wir reden hier von µC, > nicht von PCs. Nicht dass da noch irgendwelche blablubb-Ausflüchte > rauskommen. > > > Gruß, > Nick Ich weiß nicht auf was Du eigentlich raus willst. Du kannst beim Linken Speicher für die Bereiche reservieren..das wars aber schon. Damit verhinderst Du nicht das Deine Zeiger aus den vorhergesehenen Bereichen heraus laufen und das Ding crasht. Wenn Du das verhindern willst brauchst Du ne MMU mit Exeption Handling und eine Detektion von Speicherschutzverletzungen...und mußt diese handeln ..Nichts ist ja bekanntlich unmöglich. Wo ist die Grenze zwischen Embedded Controller und PC? Die kannst Du heute zu Tage frei definieren. Ein Cortex M3 läßt einen PC (Tm) ziemlich alt aussehen... Gruß, Holm
Holm T. schrieb: > Intel 8008, Stack in Hardware nicht erweiterbar..8 Bytes. Waren das nicht drei Worte (von 14 Bit Adressbreite)?
Jörg W. schrieb: > Holm T. schrieb: >> Intel 8008, Stack in Hardware nicht erweiterbar..8 Bytes. > > Waren das nicht drei Worte (von 14 Bit Adressbreite)? Freilich Adreßbreite (Kopfklatsch) http://www.cpu-world.com/CPUs/8008/ "The processor supported of 16 KB of memory (ROM and RAM combine The size of internal CPU stack was 7 levels in contrast to 3 level-stack for the i4004. The Intel 8008 could handle interrupts." Gruß, Holm BTW: Meinst Du die Bewertungen sind irgendwie sinnvoll? Ich amüsiere mich über den Primitivling der sich da wegen mir abarbeitet..
Jörg W. schrieb: > Du willst keine Umgebung benutzen, in der man die Größe des Stacks nicht > vorgeben kann, Wie willst Du zum Beispiel bei einem AVR die maximale Stackgröße vorgeben, und wer oder was soll darüber wachen daß das nicht überschritten wird und was soll dann geschehen? > und ich will keine Umgebung nutzen, in der man die Größe > des Stacks zur Compilezeit künstlich kastrieren muss. Was meinst Du mit "zur Compilezeit künstlich kastrieren müssen"?
:
Bearbeitet durch User
Bernd K. schrieb: >> Du willst keine Umgebung benutzen, in der man die Größe >> des Stacks nicht vorgeben kann, > Wie willst Du zum Beispiel bei einem AVR die maximale Stackgröße > vorgeben, und wer oder was soll darüber wachen daß das nicht > überschritten wird und was soll dann geschehen? Du kannst den Stack unter den Heap legen. Oder sogar unter den Code. Was bei einem Stack Overflow passiert, hängt dann schlicht davon ab, was der Stack gerade überschrieben hat. Und nein, ein Stack Overflow lässt sich auf Systemen ohne MMU oder MPU grundsätzlich nicht zuverlässig erkennen. Im Gegensatz zu einem "out of heap". >> und ich will keine Umgebung nutzen, in der man die Größe >> des Stacks zur Compilezeit künstlich kastrieren muss. > > Was meinst Du mit "zur Compilezeit künstlich kastrieren müssen"? Die klassische Variante, den Stack ans RAM-Ende und den Heap ans Programmende zu legen maximiert die Größe des Stacks - alles, was der Heap nicht braucht, steht zur Verfügung. Jede andere Sortierung verkleinert den Stack. Warum das ein Vorteil sein soll, wenn man einen Stack Overflow sowieso nicht erkennen kann, ist mir allerdings unklar. Das war der Ausgangspunkt der Diskussion.
S. R. schrieb: > Bernd K. schrieb: >>> Du willst keine Umgebung benutzen, in der man die Größe >>> des Stacks nicht vorgeben kann, >> Wie willst Du zum Beispiel bei einem AVR die maximale Stackgröße >> vorgeben, und wer oder was soll darüber wachen daß das nicht >> überschritten wird und was soll dann geschehen? > > Du kannst den Stack unter den Heap legen. Oder sogar unter den Code. > Was bei einem Stack Overflow passiert, hängt dann schlicht davon ab, was > der Stack gerade überschrieben hat. > > Und nein, ein Stack Overflow lässt sich auf Systemen ohne MMU oder MPU > grundsätzlich nicht zuverlässig erkennen. Im Gegensatz zu einem "out of > heap". ....nö. Mit entsperchend Aufwand kannst Du auch den Stack softwaremäßig überwachen, es gab da mal eine Bibliothek electric Fence ..oder so, die genau das tat und Dich zu Debugging Zwecken mit Infos zuschüttete.. as will aber im Normalbetrieb kein Mensch, es sei denn man hat endlos compute power.. Gruß, Holm
Holm T. schrieb: > ..oder so, die > genau das tat und Dich zu Debugging Zwecken mit Infos zuschüttete.. Wenn ich den vagen Verdacht habe daß es zu eng werden könnte dann füll ich im Startup den ganzen Stack mit 0x55, lass es ne Weile laufen, lass es alles machen was es können soll, dann halt ich es an und schau mir das RAM an. Meist reicht das um mich dahingehend wieder zu beruhigen.
:
Bearbeitet durch User
Bernd K. schrieb: > Holm T. schrieb: >> ..oder so, die >> genau das tat und Dich zu Debugging Zwecken mit Infos zuschüttete.. > > Wenn ich den vagen Verdacht habe daß es zu eng werden könnte dann füll > ich im Startup den ganzen Stack mit 0x55, lass es ne Weile laufen, lass > es alles machen was es können soll, dann halt ich es an und schau mir > das RAM an. Meist reicht das um mich dahingehend wieder zu beruhigen. Sicher doch, 0xdeadbeef geht im Zweifelsfalle auch. Electric fence findet aber auch Memory leaks. Gruß, Holm
> Und nein, ein Stack Overflow lässt sich auf Systemen ohne MMU oder MPU > grundsätzlich nicht zuverlässig erkennen. Im Gegensatz zu einem "out of > heap". Ich finde das eine sehr interessante Aussage, der ich im uebrigen zustimme. .-) Aber warum eigentlich? Es waere ja moeglich einen Controller so zu konstruieren das er nicht nur ein Register fuer die aktuelle Stackposition hat sondern auch zwei Register fuer maximal und minimalwerte und dann loesst die CPU einen Interrupt aus wenn ihre Hardware erkennt das diese Werte erreicht werden. Das waere in der Hardware doch einfach umzusetzten und der Gedanke liegt nahe. Wieso gibt es das dann nicht? Scheint noch niemanden wichtig genug gewesen zu sein. Olaf
Ich meine die C166 hatten so etwas, aber ich mag mich täuschen.
Bernd K. schrieb: > Wenn ich den vagen Verdacht habe daß es zu eng werden könnte dann füll > ich im Startup den ganzen Stack mit 0x55, lass es ne Weile laufen, lass Beitrag "Re: RAM Verbrauch auch von lokalen variablen ermitteln"
Jörg W. schrieb: > Mir fällt gerade kein einziger ein … dieses ständige Gespenst von der > Speicherfragmentierung, was immer wieder so gern an die Wand gemalt > wird, finde ich allmählich recht albern. Wer die Geschichte nicht kennt, wird sie wiederholen. Vor 30 Jahren, als PMMUs noch nicht verbaut wurden, war Speicherfragmentierung ein echtes Problem in der Praxis, vor allem auf Plattformen wie dem Amiga. Dort wurde eben einfach AllocMem() benutzt, und die haben einfach Pointer zurückgegeben, wie heute auch. Die entsprechenden Speicherbereiche waren dann im RAM festgenagelt und konnten nicht verschoben werden. Übel war bei den frühen Versionen auch, dass Speicherblöcke nicht automatisch zusammengefasst wurden. Wenn Du drei 2MB-Karten im System hattest, hat das AutoConfig eben drei 2MB Blöcke zur MemoryList hinzugefügt. 4M am Stück für eine A3-300DPI-Seite allozieren ging einfach nicht, weil zwar so viel Speicher da war, aber eben nicht am Stück. Nach Aufruf von MemMerge gings dann. Der Mac hats anders gemacht. Da musste man zuerst ein Alloc machen und hat dann nur ein Handle auf seinen Speicherblock bekommen. Wenn man den benutzen wollte, musste man ein Lock machen und hat erst dann seinen Pointer bekommen. Unlock machte den Pointer wieder ungültig, nicht aber das Handle. Das OS konnte Speicher, der nicht gelockt war, im Bedarfsfall verschieben, und genau das war der Sinn dahinter. Dieses API hats leider nicht in die C Runtime geschafft, sonst hätten wir das Problem jetzt so nicht. Ich kann mich noch gut an <Ctrl>+<Amiga>+<Amiga> zum Reboot erinnern. fchk
Frank K. schrieb: > Dieses API hats leider nicht in die C Runtime geschafft, sonst hätten > wir das Problem jetzt so nicht. Java verwenden, das macht es genau so um den Heap zu defragmentieren. S. R. schrieb: > Und nein, ein Stack Overflow lässt sich auf Systemen ohne MMU oder MPU > grundsätzlich nicht zuverlässig erkennen Wenn man auf einem Cortex -M3 den Stack an den Anfang des RAM legt, und der überläuft, bekommt man zuverlässig einen BusFault. Im Debugger kann man dann sehen wo es geknallt hat. Auch ohne die MPU zu aktivieren
Frank K. schrieb: > Ich kann mich noch gut an <Ctrl>+<Amiga>+<Amiga> zum Reboot erinnern. Alte Männer erzählen vom Krieg. ;-) (Hach, schön war die Amigazeit)
Holm T. schrieb: > Intel 8008, Stack in Hardware nicht erweiterbar..8 Bytes. Als die AVRs (AT90S1200) neu rauskamen, hatten die einen 3-Level Hardwarestack. Da habe ich mich ganz verdutzt gefragt, was der Entwickler da wohl geraucht haben möge. Und später den Rückschritt vom ATtiny22 zum ATtiny12/15 mit 3-Level Hardwarestack habe ich auch nicht verstanden. Möge der Erfinder des Hardwarestacks für alle Zeiten in der Hölle schmoren.
> Intel 8008, Stack in Hardware nicht erweiterbar..8 Bytes.
HAHA! Der war gut! :-)))
Allerdings ist die Gefahr, dass der Hardware-Stack den Heap überschreibt
extrem gering. ;-)
Gruß,
Nick
Olaf schrieb: > Es waere ja moeglich einen Controller so zu konstruieren > das er nicht nur ein Register fuer die aktuelle Stackposition > hat sondern auch zwei Register fuer maximal und minimalwerte > und dann loesst die CPU einen Interrupt aus wenn ihre > Hardware erkennt das diese Werte erreicht werden. Genau das hat Intel mit der Segmentierung im Protected Mode eingeführt: Speichersegmente haben Basis und Größe, und wenn man da rausläuft, knallt's. Das ist aber eine MMU. :-) Controller haben MPUs, die das ermöglichen und Software-Lösungen gibt es auch. Allerdings kann man das immer umgehen, wenn man es drauf anlegt. Dr. Sommer schrieb: > Wenn man auf einem Cortex -M3 den Stack an den Anfang des RAM legt, > und der überläuft, bekommt man zuverlässig einen BusFault. Das ist aus meiner Sicht eine Funktion der Buslogik, nicht der CPU - aber ja, das kann man machen. Wenn ich ein riesiges Array auf den Stack lege, kann ich aber trotzdem den BusFault vermeiden.
> Meine Erfahrung besagt, dass es für eine hinreichend komplexe > Applikation nicht möglich ist, einen Stackbedarf exakt festzulegen. So, freut mich, dass es wieder eine Diskussion gibt! Egal wie komplex, es geht. Händisch wird das aber so eklig, dass das keiner machen will. "Call Graph" sagt dir wohl was. Zeigt auf, welche Funktion was aufruft. Und das bis runter zur letzten Funktion. Also kann man sehr wohl erkennen wie tief die Verschachtelungen gehen. Für jede Funktion lässt sich der Stackbedarf einzeln ermitteln. Und mit Hilfe des Call Graphs kann man jeden Ast verfolgen und den Stackbedarf aufsummieren. Der Ast mit dem größten Stackbedarf gibt dann den worst case Stackbedarf an (+ ISRs). Da gibt es ein Tool dazu: Gimpel Software: PClint. Das kann das automatisiert. Gibt vielleicht auch andere tools, ich hab mich nicht weiter darum gekümmert. PClint nehm ich schon seit Jahrzehnten her, ist sehr lehrreich, hilfreich und pingelig. Also, der maximale Stackbedarf ist ermittelbar und ermittelt. Dann kann man genau so viel für den Stack zuweisen und braucht keine Überprüfung mehr. Und wenn der maximale Stack bekannt ist, ist auch der maximal zur Verfügung stehende Heap bekannt. Und somit ist klar, dass die Beiden sich nie überlappen werden (der heap Manager passt auf, dass er innerhalb seines zugewiesenen Bereichs bleibt). Ja, natürlich kann man weiterhin lustig über einen allokierten Bereich schreiben. Aber das ist Handwerkszeug (lässt sich auch per SW überprüfen). OK. Wenn wir uns darauf einigen können, kommt der nächste Punkt: Was passiert, wenn man den Stack vorher nicht ermittelt und den Heap nicht dran hindert in den bekanntermaßen vom Stack (irgendwann und schlimmstenfalls) benötigten Bereich reinwächst? Ich allokiere einen Block der grad dummerweise nicht in den momentanen Stack reicht (Fragmenierung usw.). Danach wird eine Funktion aufgerufen und der Stack wächst in den Heap rein. Das wollte man nicht! Und das genau ist der Punkt warum es nur richtig ist, den Stackbedarf zu ermitteln und das Maximum zu reservieren, auch auf Kosten des Heaps. Alles Andere ist nur Hoffen und hat mit Software-Engineering nichts zu tun. Gruß, Nick PS: Ich geh noch auf die anderen Punkte ein ...
Nick M. schrieb: > Egal wie komplex, es geht. > Händisch wird das aber so eklig, dass das keiner machen will. [ ] Du weißt, was Rekursion ist.
> Es geht darum, dass man bei manchen Plattformen den Stack vorab > einschränken muss, so beispielsweise bei den von Atmel gelieferten > ARM-Linkerscripts. Ich wollte Umgebungen wissen, bei denen man die Stackgröße nicht angeben kann. Wie dann der Einzelne damit umgeht liegt in seiner Verantwortung. Und wenn es ihm egal ist, dann ist ihm halt auch seine Arbeit egal.Ob derjenige am richtigen Platz sitzt ist mehr als fraglich. Gruß, Nick
S. R. schrieb: > [ ] Du weißt, was Rekursion ist. Hat er wahrscheinlich nicht, kann man in vielen Fällen auch drauf verzichten. Stackbedarf für alle Bibliotheksfunktionen unter allen x-beliebigen Umständen (gibt's gar welche mit dynamisch allozierten lokalen Variablen, deren größe von aktuellen Variablenwerten abhängt?) halte ich nicht für unmöglich, aber für einen Aufwand, den ich nur treiben würde, wenn es unbedingt sein muss (sicherheitskritische Anwendungen). S. R. schrieb: >> Wenn man auf einem Cortex -M3 den Stack an den Anfang des RAM legt, >> und der überläuft, bekommt man zuverlässig einen BusFault. > > Das ist aus meiner Sicht eine Funktion der Buslogik, nicht der CPU - > aber ja, das kann man machen. Halte ich zumindest für einen pragmatischen Ansatz. Nick M. schrieb: > Alles Andere ist nur Hoffen Naja, wenn ich (Erfahrung / Beobachtung) weiß, dass ich von meinem RAM trotz Stack und Heap sowieso nie mehr als 10 … 20 % belege, und überschauen kann, dass da nicht grundlegend mehr dazu kommt, dann wird das Hoffen zu Wissen.
> Du kannst den Stackpointer (wenn es keine MMU gibt) ja > doch nicht daran hindern, über den von dir vorab ermittelten Bereich > hinaus zu wachsen. Keine MMU, das wäre ein unfaires Hilfsmittel. ;-) Könntest du mir erklären, wieso der Stack plötzlich über den ermittelten Bereich rausgeht? Wenn das so ist, ist was an der Ermittlung falsch. Es kommen doch nicht unerwartet neue Funktionen dazu. Ja, es gibt Rekursion. Dann muss man die maximale Rekursionstiefe ermitteln (und PClint mitteilen). Und zum "geht nicht, wenn hinreichend komplex": Ich spreche von gesammt 70 kLOC (nur als Hausnummer, mir ist die LOC-Problematik bekannt). Gruß, Nick
> [ ] Du weißt, was Rekursion ist.
[ ] Du kannst lesen.
Ich hab mehrfach auf Rekursion hingewiesen. Also spar dir deine dummen
Ankreuzprüfungen, die würdest du wohl selbst nicht hinbekommen.
Nick
Nick M. schrieb: > Und mit Hilfe des Call Graphs kann man jeden Ast verfolgen und den > Stackbedarf aufsummieren. So ein Tool mit Call Graph wäre in der Tat sehr sinnvoll. Muß mal sehen, ob es sowas für den AVR-GCC gibt. Wenn ich mal so meine Programme betrachte, sind sie zu 100% rekursionsfrei. Ich mag Rekursionen nicht, sie erschweren die Verstehbarkeit sehr. Man muß dann quasi so denken, wie Münchhausen, der sich an der eigenen Stiefelstrippe aus dem Sumpf zieht. Manche bekannte Rekursionen kann der AVR-GCC sogar selber in Schleifen umformen und erspart sich damit das ganze umständliche Push/Pop + Call/Ret Geraffel. Was ich manchmal habe, sind reentrante Funktionen, die z.B. von Interrupts und Main gleichzeitg ausgeführt werden können.
> Stackbedarf für alle Bibliotheksfunktionen unter allen x-beliebigen > Umständen (gibt's gar welche mit dynamisch allozierten lokalen > Variablen, deren größe von aktuellen Variablenwerten abhängt?) halte ich > nicht für unmöglich, aber für einen Aufwand, den ich nur treiben würde, > wenn es unbedingt sein muss (sicherheitskritische Anwendungen). Also, wenn was dynamisch allokiert wird, kommt es nicht auf den Stack. Das ist schon mal sicher. Und dein ewiges "Geht nicht weil ich es nicht kenne und ist mir sowieso zu kompliziert" hilft auch niemanden weiter. Auf die Software hab ich hingewiesen, mehr kann ich für dein Krankheitsbild nicht tun. > Naja, wenn ich (Erfahrung / Beobachtung) weiß, dass ich von meinem RAM > trotz Stack und Heap sowieso nie mehr als 10 … 20 % belege, und > überschauen kann, dass da nicht grundlegend mehr dazu kommt, ... ... dann ist deine Antwort eine Themaverfehlung. Es ging darum, dass der OP eher zu wenig RAM hat. Und genau in der Situation ist es halt so, dass ... ich hab es schon oft genug erklärt. Wenn es Probleme mit dem Verständniss meiner Erklärungen gibt -> Gerne nachfragen! Gruß, Nick
Nick M. schrieb: > Und das genau ist der Punkt warum es nur richtig ist, den Stackbedarf zu > ermitteln und das Maximum zu reservieren, auch auf Kosten des Heaps. > Alles Andere ist nur Hoffen und hat mit Software-Engineering nichts zu > tun. So sieht's aus! (auch wenn ich das so noch nie gemacht habe, bin aber auch kein Softwerker und schreibe wenig Software im professionellen Bereich)
Frank K. schrieb: > Wer die Geschichte nicht kennt, wird sie wiederholen. > > Vor 30 Jahren, als PMMUs noch nicht verbaut wurden, war > Speicherfragmentierung ein echtes Problem in der Praxis, vor allem auf > Plattformen wie dem Amiga. Dort wurde eben einfach AllocMem() benutzt, > und die haben einfach Pointer zurückgegeben, wie heute auch. Die > entsprechenden Speicherbereiche waren dann im RAM festgenagelt und > konnten nicht verschoben werden. Sind auch heute noch ein Problem auf recht aktuellen embedded Linux-Systemen, je nach MMU-Implementierung der CPU. Und sowieso unter uClinux, was ohne MMU auskommen muss. Da kann man es durchaus schaffen, mit einer nicht ganz transparenten Architektur (und 'verbotenen' C++-Konstrukten) das System nach Wochen von Laufzeit zum Absturz zu bringen. Das ist dann sehr mühsam zu debuggen, bzw. muss man über die ganze Zeitdauer /proc/buddyinfo monitoren. Inzwischen gehört das bei uns zum Standard-Regresstest. Und bevor jemand auf die Idee kommt: Nein, Memory-Leaks sind ausgeschlossen. Und was den kleinen uC angeht: es ist vermutlich immer die bessere Strategie, für seinen speziellen Datenbedarf einen eigenen Pool-Allocator zu implementieren, auch wenn das Rad immer wieder neu erfunden wird.
Nick M. schrieb: > Es ging darum, dass der OP eher zu wenig RAM hat. Er hatte eher „gefühlt zu wenig RAM“. > Also, wenn was dynamisch allokiert wird, kommt es nicht auf den Stack. Hast du deine komplette Standardbibliothek und den IP-Stack selbst geschrieben, um sicher auszuschließen, dass diese sowas machen?
Fitzebutze schrieb: > Und was den kleinen uC angeht: es ist vermutlich immer die bessere > Strategie, für seinen speziellen Datenbedarf einen eigenen > Pool-Allocator zu implementieren, auch wenn das Rad immer wieder neu > erfunden wird. Nach meinen Erfahrungen hat das nur zwei Effekte: zusätzliche Bugs und weniger Effizienz (also mehr RAM-Verbrauch für gleiche Nutzungsanforderungen), damit ist es nicht „vermutlich die bessere Strategie“, sondern „mit an Sicherheit grenzender Wahrscheinlichkeit die schlechtere Strategie“.
> Ich weiß nicht auf was Du eigentlich raus willst. Du kannst beim Linken > Speicher für die Bereiche reservieren..das wars aber schon. Damit > verhinderst Du nicht das Deine Zeiger aus den vorhergesehenen Bereichen > heraus laufen und das Ding crasht. Ah, ich verstehe. Da liegt ein Missverständniss vor. Ich glaub nicht, dass dann irgendwie magisch eine MMU dazukommt, oder dass irgendwie magisch der Stack überprüft wird. Letzteres ginge per SW. Der Punkt ist, wenn ich das Maximum kenne und das zuweise, dann wird der Stack nie überlaufen. Das ist einfache Logik. Und wenn der Stack dennoch überläuft, dann würde ich gerne wissen wieso. Gruß, Nick
Nick M. schrieb: > Und wenn der Stack dennoch überläuft, dann würde ich gerne wissen wieso. Das wird aber eben ohne MMU (oder anderweitige Hardwareunterstützung der CPU) schwierig. Man könnte eine guard variable direkt unter die ermittelte Größe des Stacks legen und diese regelmäßig überwachen, aber eine Exception direkt beim Zugriff drauf bekommst du davon auch nicht.
Jörg W. schrieb: > Man könnte eine guard variable direkt unter die > ermittelte Größe des Stacks legen und diese regelmäßig überwachen, aber > eine Exception direkt beim Zugriff drauf bekommst du davon auch nicht. Man könnte schauen ob der Compiler eine Instrumentierung unterstützt, mit welcher man bei jeder Stack-Allokation eigenen Code unterbringen kann und da dann prüfen ob die Untergrenze unterschritten wird.
> Das wird aber eben ohne MMU (oder anderweitige Hardwareunterstützung der > CPU) schwierig. Das "wieso" hat sich nicht auf die Laufzeit bezogen. Ich hätte gerne eine Situation in der der Stackbedarf unerwartet mehr wird. Da wirds doch ein Beispiel dafür geben, auch wenn ich mir keines vorstellen kann (nein, nicht schon wieder Rekursion). Ich will ja dazulernen. Gruß, Nick
Nick M. schrieb: > Ich hätte gerne > eine Situation in der der Stackbedarf unerwartet mehr wird. Da wirds > doch ein Beispiel dafür geben, Nein, das gibts nicht. Ermittle aus dem Aufrufgraphen den maximalen Stackbedarf der Anwendung, ermittle ebenso den höchtmöglichen präemptiven Interruptstapel und addiere beides zusammen. Das müsste aufs Byte genau das absolute Maximum sein. Es sei denn Du hast irgendwo Rekursion, dann wirds haarig.
> Das "wieso" hat sich nicht auf die Laufzeit bezogen. Ich hätte gerne > eine Situation in der der Stackbedarf unerwartet mehr wird. DAs Problem ist das du bei komplexeren Programmen den maximalen Stackbedarf nicht sicher ermitteln kannst. Und schon garnicht mehr wenn du zehn Programmierer an einem Projekt hast die jeden Tag irgendwas neue einchecken. Olaf
Bernd K. schrieb: > Es sei denn Du hast irgendwo Rekursion, dann wirds haarig. Oder es werden variable-length arrays oder sowas wie alloca benutzt.
> Nein, das gibts nicht.
Meine Rede.
Aber es gibt hier Leute, die sagen, dass man desn Stackbedarf nicht
berechnen kann. Und dass der Stack halt einfach überlauft. Ist wohl
Schicksal! /-8
Und genau die bitte ich, mir ein Beispiel zu geben. Ich lass mich gerne
überzeugen.
Gruß,
Nick
> Oder es werden variable-length arrays oder sowas wie alloca benutzt. Und die stehen auf dem Stack? https://stackoverflow.com/questions/1018853/why-is-the-use-of-alloca-not-considered-good-practice Ja, mit C kann man perfekt Mist machen. Aber niemand zwingt dich dazu. Gut, zumindest war das ein Beispiel dafür, wie der Stack unvorhergesehen überlaufen kann. Wobei das mit dem "unvorhergesehen" noch diskussionswürdig ist. Zusätzlich bin ich mir sicher, dass PClint in dem Fall ein fettes warning rausgegeben und nicht irgend eine Größe angegeben hätte. Nick
Naja, gut, alloca() wurde ja oben genannt. Dessen Existenz hab ich ganz verdrängt.
Nick M. schrieb: >> Oder es werden variable-length arrays oder sowas wie alloca benutzt. > > Und die stehen auf dem Stack? Es ist dafür dann halt rasend schnell und man kann es eben so mal benutzen und braucht nicht den ganzen Zirkus mit heap im Linkerscript reservieren etc.
Nick M. schrieb: > Aber es gibt hier Leute, die sagen, dass man desn > Stackbedarf nicht berechnen kann. Du behauptest, dass man den maximalen Stackverbrauch immer berechnen kann. Das ist falsch. Rekursion oder alloca() auf Basis der Eingangsdaten können das ganz schnell unmöglich machen. > Und dass der Stack halt einfach überlauft. Der Stack läuft genau dann über, wenn man zuviel davon benutzt. Ob das in einer gegebenen Anwendung der Fall ist, lässt sich je nach Anwendungsdesign trivial bis unmöglich ermitteln. Maximiere ich die Größe des Stacks und er läuft trotzdem über, dann habe ich zuviel Speicher (Heap+Stack) verbraucht. Den Stack vorher künstlich zu beschränken hilft mir genau garnicht, denn die Anwendung läuft auf dem System sowieso nicht. Habe ich eine gute Abschätzung für den maximalen Stackbedarf (und den Heap), dann weiß ich, ob er überlaufen wird oder nicht. Den Stack vorher künstlich auf diese Größe zu beschränken hilft mir auch hier nicht: Es gibt kein Problem. Wenn ich den maximalen Stackverbrauch kenne, aber den maximalen Heap-Verbrauch nicht, dann gibt es möglicherweise ein Problem. Dann könnte mir der Stack möglicherweise in einen kurzzeitig zu großen Heap wachsen. Den Stack künstlich in seiner Größe zu beschränken hilft mir auch hier nicht, denn mir wächst im Zweifelsfall der Heap in den (kurzzeitig nicht benötigten) Stack rein und nicht umgekehrt. Ein Controller ohne MMU (und ohne Tricks wie BusFaults) merkt dann nicht, wenn der Stack anschließend in den Heap wächst und Daten kaputtmacht. Kurz: Dein Vorschlag, den Stack in seiner Größe zur Compilezeit einzuschränken, bringt nichts. Aber: Es ist sinnvoll, den Stackverbrauch abzuschätzen und den Heap so zu beschränken, dass immer genug freier Speicher für den Stack vorhanden ist.
:
Bearbeitet durch User
> Aber: Es ist sinnvoll, den Stackverbrauch abzuschätzen und den Heap so > zu beschränken, dass immer genug freier Speicher für den Stack vorhanden > ist. Kommst du selber drauf warum ich da jetzt lachen musste? Hint: Wenn du den heap begrenzt, dann hast du damit den Stack auch begrenzt. > Du behauptest, dass man den maximalen Stackverbrauch immer berechnen > kann. Das ist falsch. Rekursion oder alloca() auf Basis der > Eingangsdaten können das ganz schnell unmöglich machen. Ich hab die Rekursion jetzt mehrfach wiederholt. Ich mach es für dich persönlich aber hier nochmal: Rekursion ausgeschlossen. Aber selbst wenn man die verwendet, sollte man davon ausgehen, dass die irgendwann zu Ende ist. Das sollte man auch irgendwie[tm] berechnen können (ansonsten hat man den Fehler gemacht Rekursion zu verwenden ohne zu wissen ob der Stack genügt). Und bei PClint kann man die maximale Rekursionstiefe angeben, dann kann er das auch berechnen. Zu malloca: Das ist nicht im Standard. Sollte man also auch nicht verwenden. Wer es dennoch verwendet, sollte sich darüber im Klaren sein und auch genau da aufpassen. Und auch hier nochmal: PClint wird das erkennen und berücksichtigen. Geh ich stark davon aus, aber ich verwende malloca nicht. Wer mein Gefasel nicht glaubt, kann sich ja eine Demo-Version von PClint holen. Ich verwende das, seit man Software noch in echten Ringbindern mit festen Deckel verschickt hat. So mit Post und so. Und in der Deckelinnenseite waren Floppys drinnen. Falls das noch jemand kennt. Ist keine Werbung, bin nur zufrieden damit. Es gibt auch freie lint Software, aber da war mir meine Zeit zu schade. Als ich das vor paar Jahren in der Firma gekauft hab, hat es 400 € gekostet. Das ist so wie mit dem Sturzhelm: Jeder gibt dafür so viel aus wie ihm sein Kopf wert ist. Nick
Nick M. schrieb: > Hint: Wenn du den heap begrenzt, dann hast du damit > den Stack auch begrenzt. Ich fürchte, du willst einfach nicht verstehen. Ist gut, viel Spaß noch.
> Ist gut, viel Spaß noch.
Speicherplatz >= statische Variablen + Heap + Stack
Statische Variablen: fix
Heap: vorgegeben
-> sicher verwendbarer Stack vorgegeben
Auch du schaffst das!
Bis dahin frag ich mich, ob in dem Forum der Anteil an Schwallern
wirklich sooo hoch ist.
Nick
Nick M. schrieb: > Zu malloca: Das ist nicht im Standard. Sollte man also auch nicht > verwenden. Wer es dennoch verwendet, sollte sich darüber im Klaren sein > und auch genau da aufpassen. Die Funktion heißt alloca. Und ja, alloca ist nicht im c Stndard enthalten. Variable-length arrays sind es allerdings. Nick M. schrieb: > Bis dahin frag ich mich, ob in dem Forum der Anteil an Schwallern > wirklich sooo hoch ist. Vielleicht solltest du dich mal an die eigene Nase fassen und über den Tellerand schauen.
> Vielleicht solltest du dich mal an die eigene Nase fassen und über den > Tellerand schauen. Ich bemühe mich. Aber ich bekomme nur Antworten wie: * Das geht nicht -> das geht möglicherweise -> ist zu kompliziert * Ich brauch auf den Stack nicht aufpassen, weil ich vom Heap nur 10% brauch und bisher hats geklappt. Das sind keine Argumente, das ist nur ein adabei (Österreichisch für "ich red halt einfach auch mit"). Bringt auch den OP wenig, der leidet unter wenig RAM und nicht unter dem Luxusproblem "Hab keine Ahnung von Stack- und Heap-Bedarf, ich nehm einfach mehr RAM". Nick
Nick M. schrieb: >> Vielleicht solltest du dich mal an die eigene Nase fassen und über den >> Tellerand schauen. > > Ich bemühe mich. Aber ich bekomme nur Antworten wie: > * Das geht nicht -> das geht möglicherweise -> ist zu kompliziert > * Ich brauch auf den Stack nicht aufpassen, weil ich vom Heap nur 10% > brauch und bisher hats geklappt. Du hast vergessen: * Es ist nicht möglich oder gewollt mit fester Heap- und Stackgrößen zu arbeiten. Ich gebe dir ein Beispiel, mit dem ich vor ein paar Monaten zu tun hatte, bei dem zu einem Zeitpunkt der ganzen Speicher als Heap benötigt wurde und danach als Stack. Das Programm besteht aus drei Phasen. In Phase eins werden so viele Datensätze variabler Länge eingelesen, wie möglich. Die Anzahl ist durch den verfügbaren Speicher (Heap) begrenzt. In Phase zwei werden die Daten auf einen Datensatz fester Länge reduziert. In Phase drei wird der reduzierte Datensatz mit einem rekursiven Algorithmus ausgewertet. Für den Algorithmus ist garantiert, dass er nach einer endlichen, aber unbekannten Anzahl Rekursionen das Ergebnis mit gewünschter Genauigkeit berechnet. Für alle getesteten Datensätze ist die Anzahl klein genug, wobei die maximale Anzahl auch durch den verfügbaren Speicher (Stack) begrenzt ist. Wie soll man jetzt die Größe von Heap und Stack festlegen? Klar funktioniert es, wenn man einen willkürlichen Wert festlegt, nicht maximal viele Daten einliest und die Rekursion nach N Schritten ohne Ergebnis beendet. Aber warum sollte man das tun, wenn Stack und Heap nicht konkurrieren? Was ist, wenn ein Stack Overflow genauso schlimm ist, wie den Wert nicht zu berechnen?
Na bravo! Endlich mal ein Szenario das nachvollziehbar beschrieben wurde. Bringt doch deutlich mehr als ein wiederholtes "Geht nicht". So kann man weiterdiskutieren. Bei der Diskussion geht es doch darum, wie man sicherstellen kann, dass Heap und Stack nicht ineinander laufen. Jetzt ist es an der Zeit, sich den Heap anzuschauen. Es gibt verschiedene Methoden wie der implementiert wurde. First fit, best fit, rolling fit. Mag noch mehr geben. Jede hat Vor und Nachteile. First fit fängt immer am Basispointer an und sucht eine Lücke wo der angeforderte Block reinpasst. Ist eher langsam, hält den Heap aber relativ kompakt. Best fit such einen freien Block, wo der angeforderte Block mit möglichst kleiner Lücke reinpasst. Noch langsamer, noch kompakter. Rolling fit merkt sich, wo zuletzt allokiert wurde und sucht ab der Stelle weiter. Ist die schnellste Methode, führt aber dazu, dass der Heap bis zur Obergrenze benutzt wird. Bei deinem Szenarion muss man sich sicher sein, dass die letzte Methode nicht benutzt wird. Oder die Lib vom Hersteller nicht einfach geändert wird. Um dein Szenario vom Hoffen ins Wissen zu bringen, müsste man den Stackbedarf für die drei einzelnen Phasen wissen. Ist bestimmt machbar, den Aufwand kann ich nicht abschätzen. Ich glaub aber, der ist nicht immens. Weiter müsste man in der Lage sein, den Heap zur Laufzeit in der Größe verändern zu können (jeweils für jede Phase). Und man müsste den Heap untersuchen können, bis wohin allokiert wurde bevor man den Heap runtersetzt. Den Heap so zu beeinflussen kann unmöglich sein, wenn man keine sourcen hat. Oder man schreibt sich sein eigenes malloc/free/realloc/... Kann man als blöde Idee oder als gute Idee bewerten, hängt von der Situation ab. Wenn man sicher sein will/muss kommt man da wohl nicht drum rum. Wenn man den Aufwand nicht betreiben will/kann, muss man zumindest wissen wie der heap implementiert ist. Ich will hier nur auf die Gefahren aufmerksam machen. Und das Risiko steigt, je weniger RAM man hat. Wie man letztendlich damit umgeht/umgehen muss hängt von anderen Sachen ab. Gruß, Nick
Nick M. schrieb: > Ich bemühe mich. Aber ich bekomme nur Antworten wie: > * Das geht nicht -> das geht möglicherweise -> ist zu kompliziert > * Ich brauch auf den Stack nicht aufpassen, weil ich vom Heap nur 10% > brauch und bisher hats geklappt. Wenn du den Stackverbrauch abschätzen kannst, dann kannst du den Heap passend begrenzen. Dass der restliche RAM (im normalen Layout) für den Stack zur Verfügung steht, sollte klar sein. Der Stack wird also nur durch den verfügbaren RAM begrenzt. Aber: Der Heap lässt sich wesentlich einfacher zuverlässig abschätzen als der Stack, weil es eine eindeutige API gibt. Für den Stack gibt es ohne MMU keinen Schutz, bzw. maximal einen Fault. Nick M. schrieb: > Bei der Diskussion geht es doch darum, wie man sicherstellen kann, dass > Heap und Stack nicht ineinander laufen. > Jetzt ist es an der Zeit, sich den Heap anzuschauen. Wenn man den Heap wie auf einem PC benutzt, dann ist das durchaus relevant. Aber bei Mikrocontrollern mit knapp zweistellig KB sind solche Nutzungsszenarien recht unüblich und die Probleme mit ein bisschen Vorsorge leicht zu vermeiden. Nick M. schrieb: > Ich will hier nur auf die Gefahren aufmerksam machen. Du redest von Gefahren, die in den meisten Fällen nicht auftreten können. Dafür hast du sehr deutlich dafür getrommelt, den Stack grundsätzlich auf den maximalen Bedarf zu begrenzen. Das sehe ich als nicht hilfreich an, denn erstens ist es einfacher, den Heap zu begrenzen (und sicherzustellen, dass genug Stack zur Verfügung steht), zum zweiten ist das auf Systemen ohne MMU/MPU immer eine unvollständige Lösung und zum dritten hilft es trotzdem nicht, wenn man den Stackverbrauch nicht abschätzen kann. Kurz: Ja, es ist cool, wenn man weiß, wie der Heap funktioniert. Ja, es ist auch cool, wenn man weiß, wie der Stack in jeder Programmphase benutzt wird. Und Ja, es ist super, wenn man sicherstellen kann, dass das Programm nicht in einen Stack- oder Heapmangel läuft. Aber deine Lösung ist sehr viel Aufwand (und setzt kommerzielle Produkte voraus, die man vielleicht in der Firma hat - wir nicht - als Bastler definitiv nicht), setzt ein gutes Verständnis der zugrundeliegenden Implementation voraus und führt fast nie einen Vorteil gegenüber einfacher Heapkontrolle und (äußerst) grober Stackabschätzung.
S. R. schrieb: > setzt ein gutes Verständnis der zugrundeliegenden Implementation voraus Insbesondere einschließlich aller verwendeter Bibliotheken, also sowohl Stackverbrauch für jede der dort enthaltenen Funktionen als auch komplette Aufrufhierarchie der Funktionen innerhalb der Bibliothek.
:
Bearbeitet durch Moderator
Sodala, da war ja noch einiges los vor dem und am Wochenende. Ich fasse mal kurz das strittige Dilemma zusammen, mit ein paar Praxisanekdoten. Zunächst mein Eindruck, viele verschiedene hauptsächlich amerikanische Quellen ( irgendwie sind die Jungs dort im Firmware consulting viel präsenter, in Deutschland läuft das wohl alles Firmenintern ab, zumindest hab ich noch kaum jmd im Internet entdeckt der so ausführlich postet und bloggt wie ein JAck Ganssle und Michael Barr usw. ) also zu den Quellen: Ähnlich wie sich hier die MEinung herauskritallisiert: stack theoretisch abzuschätzen ist wegen Rekusrion und worst case Interrupt auftreten sehr sehr aufwendig und man braucht wohl aufwändig eingestellte Tools dazu. ( Dahingehen habe ich keinerlei praxiserfahrung, aber so wie es hier herausklang ist nur ein einziger Teilnehmer willens seinen stack vollständig rechnerisch zu bestimmen, und das wohl auch nicht bei jedem Projekt ) Also gilt wohl im allgemeinen bei kleineren uC Projekten die Faustregel: Speicherbedarf kennen und stack einfach abschätzen. Meine gelesen Faustformel: 10 % vom verfügbaren RAM erstmal sichern. Die wohl einfachste experimentelle Methode ist im CodeComposer in die system_pre_init eine Funktion einzufügen, die den Stack und heap und was sonst noch alles mit deifnierten Werten zu füllen, davon war ja hier im thread auch schon die Rede. Ich denke dass ist die Methode die 95% der User wohl verwenden werden, da sie überall zu implementieren ist ohne Fremdsoftware , vollkommen umsonst und sehr einfach für jeden nachvollziehbar. Dann Code laufen lassen und nach einiger Zeit nachsehen bis wohin Speicherzugriffe erfolgt sind, das kann man schön im memory view nachvollziehen. Danke an alle für die rege Beteiligung und dass die Diskussion nicht gänzlich aus dem Ruder gelaufen ist :)
mex schrieb: > Also gilt wohl im allgemeinen bei kleineren uC Projekten die > Faustregel: Speicherbedarf kennen und stack einfach abschätzen. Richtig. Wobei man den Stack relativ einfach grob abschätzen kann - nur mit einer exakten Bestimmung wird es sehr schnell extrem aufwändig bis unmöglich. mex schrieb: > Meine gelesen Faustformel: 10 % vom verfügbaren RAM erstmal sichern. Würde ich so nicht nehmen, denn 10% von 16 KB (Atmega1284p) sind deutlich mehr als 10% von 0.5 KB (Atmega8515). mex schrieb: > Die wohl einfachste experimentelle Methode ist im CodeComposer > in die system_pre_init eine Funktion einzufügen, die den Stack > und heap und was sonst noch alles mit deifnierten Werten zu füllen, > davon war ja hier im thread auch schon die Rede. Das funktioniert so einfach nur, wenn sich Heap und Stacknutzung nicht abwechseln. Eine weitere Alternative ist, in einem Timer-Interrupt den Stackpointer mit dem "Program Break" (also dem Ende des Heaps) zu vergleichen. Das ist zwar unzuverlässig und kostet ein bisschen CPU-Zeit, ist aber recht einfach zu implementieren und besser als nichts. Man könnte das auch erweitern und z.B. einen Stack Canary (das genannte Muster) einbauen und vergleichen. Dieses Muster liegt dann immer zwischen Stack und Heap und wandert mit. Daraus lassen sich dann z.B. reale Statistiken zur Laufzeit gewinnen, wenn man es drauf anlegt.
mex schrieb: > Dann Code laufen lassen und nach einiger Zeit nachsehen bis wohin > Speicherzugriffe erfolgt sind, das kann man schön im memory view > nachvollziehen. Man kann sich dafür auch eine kleine Funktion schreiben. Hier z.B. die Ausgabe auf meinem AT90CAN128: "gst Total 1690 Free 1597" Also sind <6% des Stacks benutzt worden seit dem letzten Aufruf. Da der AVR keine nested Interrupts unterstützt, muß man einfach nur den größten Interrupt ausführen. Rekursionen habe ich bisher auf einem MC nicht benötigt. Mir fällt dafür nur eine Anwendung ein. Wenn man auf einer SD oder nem Stick ein Filesystem mit Unterverzeichnissen nach einer Datei durchsucht. Aber ein Filesystem braucht ja eh Speicher satt. Für Ablaufsteuerungen (State-Machines) wüßte ich keine sinnvolle Benutzung von Rekursionen. Es wäre mal interessant, wenn jemand erläutern würde, wofür er eine Rekursion auf einem MC einsetzt.
Peter D. schrieb: > Da der AVR keine nested Interrupts unterstützt Macht er schon, aber wird selten benutzt, und man darf es nur außerordentlich vorsichtig nutzen. Kann aber manchmal Sinn haben.
Peter D. schrieb: > Es wäre mal interessant, wenn jemand erläutern würde, wofür er eine > Rekursion auf einem MC einsetzt. In die Versuchung kommt man typischerweise bei der Verwaltung von Binärbäumen und ähnlichem. Ich nutze z.B. so eine Struktur für SLAM (im Groben, Punkte tracken). Das System hat zwar Schutz gegen Stack-Trashing, aber keine MMU, und überdies muss garantiert sein, dass es nicht abstürzt. Deshalb darf die Rekursion nur bedingt erfolgen (auch unter Aspekten der Ausführungsdauer). Der 'uC' ist zwar etwas stärker, aber das Problem ist dasselbe wie in klein. Bei der ganzen Diskussion sollte man nicht vergessen: Wer wirklich Sicherheit garantieren will, muss es u.U. beweisen. D.h. für Safety-Standards (die sich je nach Einsatzgebiet stark unterscheiden) sind einige Sachen definitiv verboten, dazu gehört u.A. realloc(). Auf der Spülmaschinen-Statemaschine spielt das keine Rolle und im Arduino bis Rpi auch nicht. Trotzdem habe ich mir angewöhnt, gerade C-Code sauber zu simulieren. Ist erstaunlich, wie leicht man sich trotz lint/MISRA usw. ins Knie schiessen kann und was für Sicherheitslücken auftauchen können. Aber die tauchen eben erst in der vollen Simulation per 'model in the loop' auf. Dafür braucht man auch nicht unbedingt die teuren Mentor oder Cadence-Tools, das geht auch OpenSource.
Peter D. schrieb: > Es wäre mal interessant, wenn jemand erläutern würde, wofür er eine > Rekursion auf einem MC einsetzt. z.B. Parser & Interpreter für Scriptsprachen, wie z.B. Lua oder für sonstige kontextfreie "Daten-Sprachen" (XML, JSON, ...).
Peter D. schrieb: > Es wäre mal interessant, wenn jemand erläutern würde, wofür er eine > Rekursion auf einem MC einsetzt. Mathe
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.