Forum: Mikrocontroller und Digitale Elektronik Array killt Stack?


von Martin S. (sirnails)


Lesenswert?

Guten Tag miteinander,

ich bin jetzt schon ewig am Rätselraten. Ich erzeuge in der Main auf 
meinem M3 ein Array:
1
unsigned short imagedata[640];
2
3
// Zum Debuggen
4
for (i = 0; i < 640; i++){
5
    imagedata[i] = 0xFFFF;
6
};

Der Adressraum von imagedata ist: 0x20001042 bis 0x20001542
Der Stack liegt von: 0x20000d9c bis 0x2000159c

Das Array ist 0x500 groß, der Stack 0x800.

Demnach liegt das Array voll im Bereich des theoretisch möglichen. 
Scheinbar allerdings kommt es dennoch zu einer Zugriffsverletzung, weil 
mir mein Programm regelmäßig, und unvorhersebar in die Fault-ISR 
springt.

Wieso liegt das Array überhaupt im Stack? Ich habe rein rechnerisch 
genug RAM zur Verfügung, sodass es auch durchaus erst hinter 0x2000159c 
starten könnte. Außerdem soll die Größe nicht bei 640 bleiben, das Ding 
muss noch deutlich größer werden. Wie macht man dem Linker sowas 
verständlich?

Ich bräuchte da mal ein bisschen Starthilfe, ich komme damit irgendwie 
nicht zu Rande.

Vielen Dank, Gruß M.S.

von Maxx (Gast)


Lesenswert?

Martin Schwaikert schrieb:
> Wieso liegt das Array überhaupt im Stack?

Weil du imagedata lokal definierst. Es ist im Kontext der Funktion 
aktiv. Jede Aufruf der Funktion bräuchte sein eigenes imagedata sagst du 
durch die DeKlaration. Also landet es im Stackframe des Aufrufs.

> Wie macht man dem Linker sowas
> verständlich?

Ist imagedata über alle Aufrufe statisch, d.h. jeder Aufruf der Funktion 
"sieht" das gleiche imagedata, dann deklariere es auch statisch aka 
"static" Schlüßelwort.

von Jim M. (turboj)


Lesenswert?

> Zugriffsverletzung, weil
> mir mein Programm regelmäßig, und unvorhersebar in die Fault-ISR
> springt.

Stacküberlauf, vermutlich. Schau Dir mal SP in der ISR an, der zeigt 
vermutlich in den Wald...

> Wieso liegt das Array überhaupt im Stack?

Wenn das Array eine lokale Vairable in main() ist, dann landet es im 
Stack.

Lies ein C-Buch, Dir scheint wichtiges Grundlagenwissen zu fehlen.

von Martin S. (sirnails)


Lesenswert?

Jim Meba schrieb:
> Lies ein C-Buch, Dir scheint wichtiges Grundlagenwissen zu fehlen.

Dummerweise ja.

Für mich ist aber eher unverständlich, wie ich das Array auf "Übergröße" 
bekomme, ohne das der Compiler damit ein Problem hat. Es hängt also 
nicht nur an den Grundlagen, sondern daran, dass keine 
Speichersicherheit besteht.

von Karl H. (kbuchegg)


Lesenswert?

Martin Schwaikert schrieb:
> Jim Meba schrieb:
>> Lies ein C-Buch, Dir scheint wichtiges Grundlagenwissen zu fehlen.
>
> Dummerweise ja.
>
> Für mich ist aber eher unverständlich, wie ich das Array auf "Übergröße"
> bekomme, ohne das der Compiler damit ein Problem hat. Es hängt also
> nicht nur an den Grundlagen, sondern daran, dass keine
> Speichersicherheit besteht.

Doch.
Es hängt an den Grundlagen.
Wenn eine Funktion nie aufgerufen wird, werden auch keine Variablen am 
Stack angelegt. Wie aber soll der Compiler denn wissen, ob eine Funktion 
je aufgerufen wird?
Erstens sieht er in einem kompletten Programm normalerweise gar nicht 
alles, sondern immer nur Ausschnitte.
Zweitens steht ja gar nicht fest, ob der Aufruf der Funktion nicht sogar 
von irgendwelchen äusseren Einflüssen, wie zb Benutzereingaben abhängt
Drittens weiß kein Compiler dieser Welt, ob es nicht im Programm eine 
versteckte indirekte Rekursion gibt, die Funktion also mehrmals 
ineinander geschachtelt aufgerufen wird.

Wie also soll der Compiler all diese Fälle deiner Meinung nach bewerten, 
so dass er eine Aussage darüber machen kann, wieviel Speicherplatz zur 
Laufzeit in Form von lokalen Variablen benötigt wird?

Klar, bei den allereinfachsten Programmen wäre das möglich. Aber bei den 
allereinfachsten Programmen braucht das in Wirklichkeit kein Mensch. 
Interessant sind die komplexeren Fälle. Und bei denen geht es nun mal 
nicht.

: Bearbeitet durch User
von Martin S. (sirnails)


Lesenswert?

Ok, Du hast mich überzeugt :)

Das bringt mich aber damit zu einem ganz anderen Problem. Ich nutze 
Treiberbibliotheken, deren Inhalt mir unbekannt ist. Leider komme ich da 
nicht drum herum.

Wie bekomme ich heraus, wieviel Speicher mir überhaupt noch zur 
Verfügung steht? Es bringt mir ja nichts, wenn ich den Ram bis Anschlag 
vollkleistere, um hinterher keinen Speicher mehr für lokale Variablen zu 
haben :-/

von Karl H. (kbuchegg)


Lesenswert?

Martin Schwaikert schrieb:

> Verfügung steht? Es bringt mir ja nichts, wenn ich den Ram bis Anschlag
> vollkleistere, um hinterher keinen Speicher mehr für lokale Variablen zu
> haben :-/

Als allererstes nimmst du mal das Array aus der Funktion raus und machst 
es global. Dann taucht es auch in der Speicherstatistik auf.

und dann musst du halt ein wenig hasadieren. Wenn man davon ausgeht, 
dass die Treiberprogrammierer auch keine Trottel waren, dann haben die 
genau das gleiche gemacht: kein exzessiver Speicherverbrauch durch 
lokale Variablen. Ein paar Laufvariablen oder temporäre Variablen sind 
ok, aber größere Felder gehören global gemacht.
D.h. du musst 'raten' wieviel Speicher am Stack gebraucht wird. Wenn 
alle beteiligten Programmierer mitdenken, dann ist das nicht allzuviel 
benötigter Speicher.

von Martin S. (sirnails)


Lesenswert?

Karl Heinz schrieb:
> Martin Schwaikert schrieb:
>
>> Verfügung steht? Es bringt mir ja nichts, wenn ich den Ram bis Anschlag
>> vollkleistere, um hinterher keinen Speicher mehr für lokale Variablen zu
>> haben :-/
>
> Als allererstes nimmst du mal das Array aus der Funktion raus und machst
> es global. Dann taucht es auch in der Speicherstatistik auf.

Schon geschehen. Das war wirklich dämlich.

Dann hat sich die Sache hier erst einmal erledigt. Vielen Dank.

von Cyblord -. (cyblord)


Lesenswert?

Normalerweise reservieren C Libraries aus diesem Grund eher wenig 
Speicher selbst. Klassisch muss man das ja selbst tun (in dem man z.B. 
eine Struktur der lib in seinem Code in einer Variablen anlegt) und dann 
einen Zeiger audarauf an die lib übergibt. Somit hat man den Speicher 
voll unter Kontrolle.

gruß cyblord

von SeriousSam (Gast)


Lesenswert?

Karl Heinz schrieb:
> ok, aber größere Felder gehören global gemacht.
> D.h. du musst 'raten' wieviel Speicher am Stack gebraucht wird. Wenn
> alle beteiligten Programmierer mitdenken, dann ist das nicht allzuviel
> benötigter Speicher.

Da muss man nicht raten. Es gibt Tools, die das berechnen. 
Beispielsweise der Stackanalyzer von absInt.

von Steffen R. (steffen_rose)


Lesenswert?

printf() Implementierungen legen häufig ihren Puffer auf den Stack.

Die Stackbelastung kann man auch (mit gewisser Unsicherheit) auch selbst 
herausbekommen.

- Bei main() anhalten bzw. nachdem die Speicherinitialisierung erfolgt 
ist
- Stackbereich mit einem Test-Wert füllen
- Programm laufen lassen
- Programm anhalten
- Stack im Memorydump ansehen

Als Zugabe sollte man mindestens noch den Stackbedarf der Interrupte 
hinzurechnen. Der Test ist meist zu kurz, dass die Interrupte wirklich 
genau dann auftreten, wenn das Hauptprogamm seinen Maximalbedarf an 
Stack nutzt.

von Martin S. (sirnails)


Lesenswert?

Der Stack ist momentan bei 2096 Byte, wobei dieser Bedarf von TI so 
festgesetzt wurde. Ich sollte ja ursprünglich auf abstrakter Ebene das 
Beispielprogramm entmüllen und erweitern. Dass ich mich jetzt um 
elementare Kernpunkte kümmern muss, war eigentlich nicht Bestandteil der 
Aufgabenstellung.

von Ben W. (ben_w)


Lesenswert?

Steffen Rose schrieb:
> - Bei main() anhalten bzw. nachdem die Speicherinitialisierung erfolgt
> ist
> - Stackbereich mit einem Test-Wert füllen
> - Programm laufen lassen
> - Programm anhalten
> - Stack im Memorydump ansehen

das ist die gängige methode
besonders beliebt ist der Test-Wert 0xaffe
weil Affen auch nichts anderes amchen als da zu sein und bannen futtern 
:)

von Steffen R. (steffen_rose)


Lesenswert?

Martin Schwaikert schrieb:
> Dass ich mich jetzt um
> elementare Kernpunkte kümmern muss, war eigentlich nicht Bestandteil der
> Aufgabenstellung.

Dann hattest Du nicht die Aufgabenstellung ein embedded Programm zu 
erstellen?

Und den Punkt Stack hast Du selbst ins Gespräch gebracht. 2k sind in 
vielen Fällen reichlich. Hinweise dazu, den Verbrauch gering zu halten 
wurden genügend hier gegeben.

von Karl H. (kbuchegg)


Lesenswert?

SeriousSam schrieb:
> Karl Heinz schrieb:
>> ok, aber größere Felder gehören global gemacht.
>> D.h. du musst 'raten' wieviel Speicher am Stack gebraucht wird. Wenn
>> alle beteiligten Programmierer mitdenken, dann ist das nicht allzuviel
>> benötigter Speicher.
>
> Da muss man nicht raten. Es gibt Tools, die das berechnen.
> Beispielsweise der Stackanalyzer von absInt.

Ach.
Und woher weiß dein Stackanalyzer zb meine Eingaben ins Programm, die 
sich durchaus auch so auswirken können, dass Funktionen überhaupt nicht 
aufgerufen werden oder ihren Speicherbedarf anhand meiner Eingabe (oder 
einer Eingabe von irgendeiner anderen Schnittstelle) adaptieren?
Letzten Endes musst du immer irgendwelche (begründete) Annahmen treffen, 
wenn du den Stackverbrauch abschätzen musst. Selbst beim Austesten durch 
Analyse des Speicherabbilds muss man das. Die Annahme lautet dann: mein 
Test ist ein repräsentativer Querschnitt durch das, was dem Programm im 
realen Einsatz begegnen wird.
Gerad an dieser Stelle ist der blinde und unkritische Einsatz von Tools 
ein zweischneidiges Schwert.

: Bearbeitet durch User
von Stefan (Gast)


Lesenswert?

Einen groben Anhaltspunkt zum freien (heap) Speicher bekommst Du, indem 
Du wiederholt mit malloc() Speicher belegst und mit free() wieder 
freigibst. Du fängst mit 16 Bytes an und machts den Block dann bei jedem 
Versuch um 50% größer. Irgendwann wird malloc() fehlschlagen, nämlich 
wenn Du zu weit gegangen bist.

Wenn Stack und Heap sich den Speicher Teilen, erfährst Du so ungefähr, 
wieviel RAM frei ist.

Das ist aber wirklich nur ein grober Anhaltspunkt. Denn der Speicher 
kann fragmentiert sein und so zum Beispiel keine 400 Bytes am Stück mehr 
frei haben, dafür aber 10 mal 200 Bytes.

Letztendlich sollte Dir (spätestens jetzt) klar sein, dass C keine 
ausgefuchste Speicherverwaltung enthält. Da läuft weder ein Windows 
drunter, noch eine VM. Wir sind hier nicht in der Java/.NET Welt, wo 
eine durchdachte Speicherverwaltung viele Fehlerfälle verhindert und 
wenns doch passiert, eine aussagekräftige Fehlermeldung liefert.

Das kannst Du Dir natürlich programmieren, du musst nur malloc() und 
free() neu implementieren, ALLE lokalen Variablen damit erzeugen und in 
jeder Funktion prüfen, ob noch genug Stack frei ist, um weitere 
Unterfunktionen aufzurufen.

Nur läuft das ganze dann auch so lahm wie Java.

von Udo S. (urschmitt)


Lesenswert?

Stefan schrieb:
> Das kannst Du Dir natürlich programmieren, du musst nur malloc() und
> free() neu implementieren,

Hmm, ausser dir hat hier niemand über dynamische Speicherallokation 
geredet.

Stefan schrieb:
> Nur läuft das ganze dann auch so lahm wie Java.

Der Fehler sitzt meistens vor der Tastatur :-)

von Stefan (Gast)


Lesenswert?

Wenn Stack und Heap sich den Speicher teilen (wie z.B. bei AVR üblich), 
dann hat der Stack eine dynamische Größe und dann (und nur dann) hilft 
meine Methode, beide Speicherbereiche zugleich zu überwachen.

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.