Premiere ! Meine erste Frage hier.
Folgendes Problem:
Ich definiere einen lokalen Puffer in main()
#define BUFFER_SIZE 8192
int main(void)
{
unsigned char buffer[BUFFER_SIZE];
.....
Das Programm schmiert ab. Sieht nach Stack Überlauf aus.
Je nachdem wie groß BUFFER_SIZE definiert wird stürzt
es an unterschiedlichen Stellen ab.
Mit BUFFER_SIZE 512 läuft es ohne Probleme.
Wird buffer[] global definiert läuft alles einwandfrei !
#define BUFFER_SIZE 8192
unsigned char buffer[BUFFER_SIZE];
int main(void)
{
.....
Der LPC2136 hat 32kB RAM, und mein Programm braucht nicht mal
1,3kB davon. Abgesehen vom großen buffer[]. Zusammen also
sagen wir mal 10kB.
Mit lokalem buffer[] bekomme ich folgende Speicherbelegung:
.text 18508 0
.data 4 1073741824
.bss 1316 1073741828
.stack 1024 1073743360
Mit globalem buffer[] sieht das so aus:
.text 18440 0
.data 4 1073741824
.bss 9508 1073741828
.stack 1024 1073751552
Werden lokale Puffer auf dem Stack angelegt ?
Das würde die Abstürze erklären.
Hi Holger,
sofern du nicht static davor schreibst, liegt die Variable i.allg. auf
dem Stack (machen zumindest C Compiler so).
Mit static kann sie dort eigentlich nicht mehr liegen, weil der Wert der
Variablen zwischen Aufrufen erhalten bleibt (d.h. diese Variable kann
nur durch diese Funktion verändert werden).
P.S.: auf den Heap kannst du in C nur mit Hilfe der Funktionen für die
dynamische Speicherverwaltung zugreifen. Das Stackgeschiebe rechnet der
Compiler beim Übersetzen der Funktion aus. Wieviel Stack jedoch
verfügbar ist, wird normalerweise erst zur Laufzeit festgelegt. Im
allgemeinen ist zum Übersetzungszeitpunkt noch nicht klar, wie die
Laufzeitumgebungen einer Funktion dimensioniert sind.
Hallo Micha,
mit static funktioniert es jetzt auch lokal.
Und der reale Speicherbedarf wird auch angezeigt.
.text 18440 0
.data 4 1073741824
.bss 9508 1073741828
.stack 1024 1073751552
DANKE !
Hallo,
ich hatte vor kurzen das gleiche Problem. Aber ich versteh immer nocht
nicht, wie das sein kann. Wenn der Stack nach unten wächst, dann belegt
er imer mehr Platz des Heaps, bis dieser ausgeht. Erst dann kommt es zu
einer Kollosion zwischen dem Stack und den statischen Variablen. Wenn
der LPC2136 32kb RAM hat und wie oben 1316 Bytes für statische Variablen
braucht und 1024 Bytes für den Stack berechnet wurden, denn bleibt ja
theoretisch 32768-1316-1024=30428 Bytes übrich für den Heap. Wie kann es
dann sein, dass der Stack in .bss reinwächst?
MfG Mark
WinARM selbst definiert kein Speicherlayout, das bleibt dem Anwender mit
seinem individuellen Startup-Code und Linker-Script überlassen. Da
holger darüber nicht informiert hat, kann hier nur spekuliert werden.
Wenn Du Dein Array innerhalb der Funktion deklarierst, ist es eine
sogenannte "automatische Variable". Und die landen nicht auf dem Heap,
sondern auf dem Stack.
Einen Heap gibt es erst, wenn dynamische Speicherverwaltung mit
malloc/new durchgeführt wird.
Dein Stack ist 1 kByte groß, Deine Variable aber ist 8 kByte groß.
Variablen, die außerhalb einer Funktion deklariert werden, sind keine
automatischen Variablen und landen daher nicht auf dem Stack. Ebenso
statische Variablen, die innerhalb einer Funktion deklariert werden.
Beim Stack hat man die Wahl wohin man ihn legt.
- Eine gängige Version ist die von Mark skizzierte - am oberen Ende des
Speichers anfangend abwärts gegen den Heap laufend. Vorteil: Sowohl Heap
wie auch (System/User-) Stack können den gesamten Speicher nutzen, ohne
vorher Limits festlegen zu müssen.
- Die andere Variante legt ihn ans Ende des definierten Datenbereichs,
und den Heap dahinter. Das ist die hier verwendete. Damit ist die Grösse
aller Stacks zusammen entsprechend begrenzt.
Entschieden wird das im Zusammenspiel von Linker-Script und
Startup-Code.
Hallo Andreas,
vielen Dank für die Erklärung. Jetzt verstehe ich auch das Problem, denn
ich wusste nicht, dass es so etwas wie die zweite von Dir geschilderte
Variante gibt. Wie kann man denn im Linkerscript einstellen, dass der
Stack ganz am Ende platziert werden muss?
MfG Mark
> Wie kann man denn im Linkerscript einstellen, dass der> Stack ganz am Ende platziert werden muss?
Der Stack wird im Startup-Code geladen. Entscheidend ist womit. In hier
gezeigten Fall wird das Symbol "_stack" dazu verwendet, das im
Linker-Script am Ende der .stack Section plaziert wird.
Alternativ kann man im Linker-Script die .stack Section auch ganz
weglassen, und das entsprechende Symbol statt dessen direkt ans Ende vom
RAM nageln:
_stack = 0x40008000;
Wobei man das im Startup-Code auch direkt so machen kann. Ich persönlich
neige jedoch dazu, den Startup-Code innerhalb der LPC2000-Reihe gleich
zu halten und die Abhängigkeiten vom jeweiligen Modell in Linker-Scripte
zu verlagern. Wobei ich das Linker-Script aufteile:
LPC2119.ld LPC2129.ld LPC2106.ld ... definert die MEMORY Areas und eben
des Anfang der Stacks (5-zeiler).
ROM.ld RAM.ld definiert basierend darauf die Sections (ROM-basiert und
RAM-basiert) und ist modellunabhängig. Im Makefile stehen dann einfach
beide Scripte nacheinander drin.
Auch wenn der Stack ganz am Ende anfängt, kann allzu bedenkenloser
Umgang mit Stack Ärger verursachen: nämlich dann, wenn mehrere Tasks
laufen und es keine Stacküberwachung gibt (häufig). Bei Erzeugen der
Task muss normalerweise die Grösse des Stacks angegeben werden, mit dem
die Task auskommen muss. Der Stack der nächsten Task fängt dann
mittelbar (z.B. wg.TCB) oder unmittelbar danach an. Die Task die über
ihren Stack hinausschreibt arbeitet dann zwar noch korrekt, aber sobald
die Task, deren Stack sich anschliesst und der überschrieben wurde, den
Prozessor zugeteilt bekommt, kommt's zu unvorhersehbarem Verhalten bzw.
Abstürzen.