Forum: Mikrocontroller und Digitale Elektronik Wer benutzt heap Speicher in uCs < 16 kB RAM?


von mex (Gast)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

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.

von Olaf (Gast)


Lesenswert?

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

von Jim M. (turboj)


Lesenswert?

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.

von Roland D. (roland_d829)


Lesenswert?

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>

von Sebastian S. (amateur)


Lesenswert?

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
von Falk B. (falk)


Lesenswert?

Roland D. schrieb:
> Mittlerweile habe ich alleine in unserer Firma 5 verschiedene Memorypool
> implementierungen gesehen -.-"

https://de.wikipedia.org/wiki/Not-invented-here-Syndrom

von mex (Gast)


Lesenswert?

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.

von Falk B. (falk)


Lesenswert?

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."

von Patrick C. (pcrom)


Lesenswert?

(PSOC3/PSOC5lp)
Heap benutze ich selber nicht aber es gibt libraries die ich benutze die 
ein minimalen HEAP erfordern

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


Lesenswert?

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
von mex (Gast)


Lesenswert?

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 :)

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


Lesenswert?

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
von Peter D. (peda)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

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.

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


Lesenswert?

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
von Stefan F. (Gast)


Lesenswert?

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.

von S. R. (svenska)


Lesenswert?

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).

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


Lesenswert?

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.

von PittyJ (Gast)


Lesenswert?

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.

von Olaf (Gast)


Lesenswert?

> 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

von Peter D. (peda)


Lesenswert?

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.

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


Lesenswert?

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.

von m.n. (Gast)


Lesenswert?

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.

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


Lesenswert?

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 …

von Holm T. (Gast)


Lesenswert?

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

von Einer K. (Gast)


Lesenswert?

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.

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


Lesenswert?

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.

von nickm (Gast)


Lesenswert?

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

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


Lesenswert?

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
von Roland E. (roland0815)


Lesenswert?

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.

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


Lesenswert?

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.

von nickm (Gast)


Lesenswert?

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

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


Lesenswert?

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.

von mex (Gast)


Lesenswert?

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.

von nickm (Gast)


Lesenswert?

> 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

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


Lesenswert?

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.

von nickm (Gast)


Lesenswert?

> 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

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


Lesenswert?

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.

von nickm (Gast)


Lesenswert?

> 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

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


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

> 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

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


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

> 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

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


Lesenswert?

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
von Holm T. (Gast)


Lesenswert?

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

von Holm T. (Gast)


Lesenswert?

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

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


Lesenswert?

Holm T. schrieb:
> Intel 8008, Stack in Hardware nicht erweiterbar..8 Bytes.

Waren das nicht drei Worte (von 14 Bit Adressbreite)?

von Holm T. (Gast)


Lesenswert?

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..

von Bernd K. (prof7bit)


Lesenswert?

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
von S. R. (svenska)


Lesenswert?

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.

von Holm T. (Gast)


Lesenswert?

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

von Bernd K. (prof7bit)


Lesenswert?

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
von Holm T. (Gast)


Lesenswert?

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

von Olaf (Gast)


Lesenswert?

> 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

von Schnauze (Gast)


Lesenswert?

Ich meine die C166 hatten so etwas, aber ich mag mich täuschen.

von Falk B. (falk)


Lesenswert?

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"

von Frank K. (fchk)


Lesenswert?

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

von Dr. Sommer (Gast)


Lesenswert?

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

von Falk B. (falk)


Lesenswert?

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)

von Peter D. (peda)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

> 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

von S. R. (svenska)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

> 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 ...

von S. R. (svenska)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

> 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

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


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

> 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

von Nick M. (Gast)


Lesenswert?

> [ ] 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

von Peter D. (peda)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

> 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

von Falk B. (falk)


Lesenswert?

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)

von Fitzebutze (Gast)


Lesenswert?

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.

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


Lesenswert?

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?

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


Lesenswert?

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“.

von Nick M. (Gast)


Lesenswert?

> 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

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


Lesenswert?

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.

von Dr. Sommer (Gast)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

> 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

von Bernd K. (prof7bit)


Lesenswert?

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.

von Olaf (Gast)


Lesenswert?

> 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

von mh (Gast)


Lesenswert?

Bernd K. schrieb:
> Es sei denn Du hast irgendwo Rekursion, dann wirds haarig.

Oder es werden variable-length arrays oder sowas wie alloca benutzt.

von Nick M. (Gast)


Lesenswert?

> 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

von Nick M. (Gast)


Lesenswert?

> 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

von Bernd K. (prof7bit)


Lesenswert?

Naja, gut, alloca() wurde ja oben genannt. Dessen Existenz hab ich ganz 
verdrängt.

von Bernd K. (prof7bit)


Lesenswert?

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.

von S. R. (svenska)


Lesenswert?

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
von Nick M. (Gast)


Lesenswert?

> 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

von S. R. (svenska)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

> 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

von mh (Gast)


Lesenswert?

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.

von Nick M. (Gast)


Lesenswert?

> 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

von mh (Gast)


Lesenswert?

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?

von Nick M. (Gast)


Lesenswert?

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

von S. R. (svenska)


Lesenswert?

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.

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


Lesenswert?

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
von mex (Gast)


Lesenswert?

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 :)

von S. R. (svenska)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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.

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


Lesenswert?

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.

von Fitzebutze (Gast)


Lesenswert?

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.

von Dr. Sommer (Gast)


Lesenswert?

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, ...).

von mh (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.