Bezüglich Stack, Heap, Ram etc. auf µC

Gast #3768208
Lesenswert?

Hallo Experten,

ich habe ein paar grundsätzliche Fragen zum Programmieren eines µC. Ich 
selbst programmiere µC auf ARM Cortex-M4 Basis, falls das bei den Fragen 
eine Rolle spielt.

- Werden lokale Variablen werden sofort nach Start des µC in den SRAM 
gelegt? Liegen diese liegen im Stack?
- Globale Variablen werden auch sofort nach Start in den SRAM gelegt? 
Diese liegen glaube nicht im Stack, ist das richtig?
- Wo sind constant Variablen allokiert? Liegen diese im Flash?
- Gibt es Richtlinien zur Dimensionierung des Stacks? Wodurch merkt man 
wann er zu klein ist?

Vielen Dank für eure Antworten!!
#3768218
Lesenswert?

Hallo,

> Gibt es Richtlinien zur Dimensionierung des Stacks? Wodurch merkt man
> wann er zu klein ist?

Theoretisch kannst du den Stack exakt bemessen. Zunächst mal: mach ihn 
so gross wie möglich. Dann fülle ihn mit einem bestimmten Bitmuster, 
z.B. 0xAFFEAFFE oder 0xDEADBEEF oder so etwas. Und dann lass einfach 
deine Software laufen. Später kannst du dann z.B. mit dem Debugger 
schauen, bis wie weit herunter dein Bitmuster überschrieben wurde, so 
eine Art 'Wassermarke' oder sowas in der Art. Das war der grösste 
Stackverbrauch. Einerseits könnte man den also so bemessen. Andererseits 
könntest du auf dem PC in deiner Entwicklungsumgebung z.B. mit Doxygen 
oder so einen Callgraph erzeugen lassen und die tiefste Verschachtelung 
von Funktionen bestimmen. Da jeder Funktionsaufruf einen bestimmten 
Stackverbrauch mit sich zieht, kann man dann so den Stackverbrauch 
abschätzen.

> Wo sind constant Variablen allokiert? Liegen diese im Flash?

ja.

> Globale Variablen werden auch sofort nach Start in den SRAM gelegt?
> Diese liegen glaube nicht im Stack, ist das richtig?

Nein, weil sie global sind. Die liegen 'irgendwo' im Memory. Je nach dem 
ob sie mit 0 initialisiert werden oder nicht liegen sie im BSS-Segment 
oder aber in einem anderen Segment, dessen Name mir jetzt nicht einfällt 
und das durch die Kopierereri im Startup-Code initialisiert wird. 
Genauso wie die lokalen static-Variablen in Funktionen. Die liegen im 
selben Segment. Aber 'normale' auto-Variablen liegen auf dem Stack.


Gruss
Tobias


Edit: Peter II war schneller. Er hat natürlich recht, aber ich gehe mal 
von C aus ;-)
#3768223
Lesenswert?

Tob75 schrieb:
> - Werden lokale Variablen werden sofort nach Start des µC in den SRAM
> gelegt? Liegen diese liegen im Stack?

Lokale Variablen liegen im Stack und werden dann angelegt, wenn sie im 
Programmcode deklariert werden.

> - Globale Variablen werden auch sofort nach Start in den SRAM gelegt?
> Diese liegen glaube nicht im Stack, ist das richtig?

Globale Variablen werden beim Start in den SRAM gelegt und zwar nicht in 
den Stack, sondern in einen separaten Bereich für globale Variablen. 
(Optimierungen und Eigenheiten des Compilers ausgenommen.)

> - Wo sind constant Variablen allokiert? Liegen diese im Flash?

Kann, muss aber nicht sein. Wenn der Mikrocontroller nicht unkompliziert 
direkt wahlfrei aus dem Flash lesen kann, so wird man sie eher im RAM 
anlegen.

> - Gibt es Richtlinien zur Dimensionierung des Stacks? Wodurch merkt man
> wann er zu klein ist?

Typischerweise wächst der Stack vom oberen Ende des Speichers nach 
unten, während alles andere von unten nach oben wächst. So kollidieren 
der Stack erst mit dem Rest des Speichers, wenn der Speicher randvoll 
ist. Das merkst du daran, dass dein Programm komplett verrückt spielt.

Den Stack "dimensioniert" man somit eigentlich nicht. Man muss bloss 
sicherstellen, dass er nie zu gross wird. Auf einem ARM dürfte das aber 
ohne Rekursion fast nicht passieren.
#3768284
Lesenswert?

Tob75 schrieb:
> - Wo sind constant Variablen allokiert? Liegen diese im Flash?

Diese Frage hängt stark von der verwendeten Plattform und den 
verwendeten Werkzeugen ab. AVRs mit ihrer Harvard Architektur werden 
auch das im RAM ablegen. Hier bedarf es einiger Tricks (Attribute 
PROGMEM), um das Ganze wirklich in den Flash-Speicher zu bekommen.

"const" ist überhaupt nur ein Hinweis an den Compiler (und an 
Mitmenschen), dass der Inhalt der Variable nicht verändert wird. Dadurch 
kann der Compiler vor Dummheiten warnen und ggf. Optimierungen 
durchführen. In erster Linie kann man sich das aber wie "normale" 
Variablen vorstellen.

Mit freundlichen Grüßen,
Karol Babioch
Gast #3768345
Lesenswert?

Tobias Plüss schrieb:

> Theoretisch kannst du den Stack exakt bemessen. Zunächst mal: mach ihn
> so gross wie möglich. Dann fülle ihn mit einem bestimmten Bitmuster,
> z.B. 0xAFFEAFFE oder 0xDEADBEEF oder so etwas. Und dann lass einfach
> deine Software laufen. Später kannst du dann z.B. mit dem Debugger
> schauen, bis wie weit herunter dein Bitmuster überschrieben wurde, so
> eine Art 'Wassermarke' oder sowas in der Art. Das war der grösste
> Stackverbrauch.

Gefährlicher Ansatz. Hierbei ist nicht sichergestellt, daß der worst 
case während der Prüfzeit überhaupt aufgetreten ist. Also besser ganz 
schnell wieder vergessen.

> Andererseits
> könntest du auf dem PC in deiner Entwicklungsumgebung z.B. mit Doxygen
> oder so einen Callgraph erzeugen lassen und die tiefste Verschachtelung
> von Funktionen bestimmen.

Schon besser, aber immer noch nur die halbe Miete. Der Stackverbrauch 
von Interrupts muß gesondert berücksichtigt werden.
#3768349
Lesenswert?

c-hater schrieb:
>> Andererseits
>> könntest du auf dem PC in deiner Entwicklungsumgebung z.B. mit Doxygen
>> oder so einen Callgraph erzeugen lassen und die tiefste Verschachtelung
>> von Funktionen bestimmen.
>
> Schon besser, aber immer noch nur die halbe Miete. Der Stackverbrauch
> von Interrupts muß gesondert berücksichtigt werden.

zzgl. Rekursion...
#3768355
Lesenswert?

c-hater schrieb:
>> Später kannst du dann z.B. mit dem Debugger schauen, bis wie weit
>> herunter dein Bitmuster überschrieben wurde, so eine Art 'Wassermarke'
>> oder sowas in der Art. Das war der grösste Stackverbrauch.
>
> Gefährlicher Ansatz. Hierbei ist nicht sichergestellt, daß der worst
> case während der Prüfzeit überhaupt aufgetreten ist.

Immer noch besser als die Art, wie es normalerweise ermittelt wird, 
nämlich gar nicht.

>> Andererseits könntest du auf dem PC in deiner Entwicklungsumgebung z.B.
>> mit Doxygen oder so einen Callgraph erzeugen lassen und die tiefste
>> Verschachtelung von Funktionen bestimmen.
>
> Schon besser, aber immer noch nur die halbe Miete. Der Stackverbrauch
> von Interrupts muß gesondert berücksichtigt werden.

Und den Aufwand gibst du dir für jedes Programm?
#3768357
Lesenswert?

Mal von C und ähnlichen Sprachen ausgehend, üblicherweise:

Tob75 schrieb:
> - Werden lokale Variablen werden sofort nach Start des µC in den SRAM
> gelegt?

Nein. Der Platz wird meist am Anfang einer Funktion auf dem Stack 
angelegt und mit Beendigung wieder freigegeben. Drum ist es ein Stack.

Ausnahme: Lokale Variablen mit "static" dran. Die sind vom Speicher her 
wie globale Variablen, aber nur in der Funktion selbst per Name 
ansprechbar.

> Liegen diese liegen im Stack?

Ja. Ausnahme sind kleine µC wie z.B. 8051 und PIC16, die sich mit der 
Verwendung von Stacks als Speicher für Variablen etwas schwer tun, und 
deren Compiler deshalb auch lokale Variablen gerne statisch anlegen, 
ggf. überlappend.

> - Globale Variablen werden auch sofort nach Start in den SRAM gelegt?
> Diese liegen glaube nicht im Stack, ist das richtig?

Ja.

> - Wo sind constant Variablen allokiert? Liegen diese im Flash?

Keine generelle Regel. Aber als Daumenregel kann man wohl sagen, dass 
dies nur der Fall ist, wenn das Flash im Datenadressraum liegt. Bei 
32-Bittern ist das üblich, bei kleineren teils nicht.

> - Gibt es Richtlinien zur Dimensionierung des Stacks? Wodurch merkt man
> wann er zu klein ist?

Das er überläuft. :-)

Im Ernst: es gibt keine Regel dafür wie sich das äussert. In vielen 
Umgebungen laufen Stack und Heap / statische Daten gegeneinander, 
weshalb ein überbordender Stack zunächst in den Heap und dann in die 
statischen Daten reinläuft und da Chaos anrichtet. Aber es gibt auch 
andere Umgebungen.
#3768518
Lesenswert?

Steffen Rose schrieb:
> konstante auto-Variable liegen (immer?) auf dem Stack.

Oder nirgends. Oder werden statisch angelegt, weil sie als 
unveränderlich nicht reentrant sein müssen:
1
int g(const int *);
2

3
int f(void)
4
{
5
        const int x[2] = { 1, 2 };
6
        return g(x);
7
}
1
f:
2
        subl    $12, %esp
3
        movl    $x.1281, (%esp)
4
        call    g
5
        addl    $12, %esp
6
        ret
7
x.1281:
8
        .long   1
9
        .long   2

Lokale Variablen liegen je nach Prozessor auch in Registern, ohne dass 
auf dem Stack dafür Platz angelegt werden muss. Oder sie liegen 
nirgends, weil vollständig wegoptimiert.
#3768574
Lesenswert?

@ P. M. (o-o)

>Typischerweise wächst der Stack vom oberen Ende des Speichers nach
>unten, während alles andere von unten nach oben wächst. So kollidieren
>der Stack erst mit dem Rest des Speichers, wenn der Speicher randvoll
>ist. Das merkst du daran, dass dein Programm komplett verrückt spielt.

Naja, manchmal hat man aber auch Pech und es kommt zu Fehlern, die lange 
nicht auffallen, z.B. weil eine maximale Stacktiefe nur selten erreicht 
wird oder nur kleine Fehler passieren aber kein Absturz bzw. massives 
Fehlverhalten. Wäre das nicht ein wichtiges Feature für CPUs, eine 
Stackprüfung in HARDWARE? Oder wenigstens in Software? Und damit meine 
ich nicht den Trick mit dem Füllmuster, sondern eine automatische 
Prüfung des Stacks bei jedem Funktionsaufruf!
Gast #3768613
Lesenswert?

Falk Brunner schrieb:
> Wäre das nicht ein wichtiges Feature für CPUs, eine
> Stackprüfung in HARDWARE? Oder wenigstens in Software? Und damit meine
> ich nicht den Trick mit dem Füllmuster, sondern eine automatische
> Prüfung des Stacks bei jedem Funktionsaufruf!
Ja, das fände ich auch wichtig. Ein Stack-Underrun-Interrupt bzw. eine 
Exception sollte sich mit einem einfachen Compare realisieren lassen.

Gibt es Architekturen, die sowas schon unterstützen?

Wie sieht eine brauchbare Realisierung in Software aus?
Eine Abfrage bei jedem Funktionsaufruf dürfte zuviel Performance kosten.

Mark
#3768619
Lesenswert?

Falk Brunner schrieb:
> Wäre das nicht ein wichtiges Feature für CPUs

Ausserhalb der kleinen µC-Sphäre lässt sich das oft mühelos über die 
Speicherverwaltung erledigen. Also indem der Stack sein Ende in einem 
finalen page fault findet. Der Compiler muss dann nur drauf achten, dass 
bei grossen lokalen Daten die guard page nicht übersprungen wird, muss 
also ggf. bei Arrays entsprechende Zugriffe alle paar KB einflechten.

> Oder wenigstens in Software?

Gibts in GCC im Prinzip, wobei es natürlich Sache der 
Zielsystemanpassung ist, etwas draus zu machen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren