Forum: PC-Programmierung Stack vs Heap?


von Joerg69 (Gast)


Lesenswert?

Was ist der genaue Unterschied zwischen Stack und Heap? Im Bezug auf C 
und Pointer... ich habe den Unterschied immer noch nicht ganz 
verstanden, und Wikipedia verwirrt auch nur.

Danke!

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


Lesenswert?

Das eine wird automatisch verwaltet und enthält alle die lokalen (pro 
Funktion) Daten, die nicht in Registern gehalten werden können. Außerdem 
werden bei Unterprogrammaufrufen dort Werte gesichert, die vom 
Unterprogramm zerstört werden könnten.

Heap enthält die Datenbereiche, die mit malloc() angefordert wurden.

von Kevin M. (arduinolover)


Lesenswert?

Um es einfach zu machen würde ich sagen man kann es so umschreiben:

Der Stack enthält alles was der Linker an Speicher zuweist.

Heap ist das was zur Laufzeit zugewiesen wird, z.B. siehe oben (malloc)

Falls wer Einwende hat nur her damit :P

von Nop (Gast)


Lesenswert?

Kevin M. schrieb:

> Der Stack enthält alles was der Linker an Speicher zuweist.

Nein, das verwechselst Du mit BSS/DATA.

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


Lesenswert?

Kevin M. schrieb:
> Heap ist das was zur Laufzeit zugewiesen wird, z.B. siehe oben (malloc)

Stack wird auch "zur Laufzeit zugewiesen", nur eben implizit, nicht 
explizit wie bei malloc().

von Rolf M. (rmagnus)


Lesenswert?

Stack und Heap sind streng genommen Implementationsdetails des 
Compilers. Auf C-Ebene haben die Begriffe erst mal keine Bedeutung. Dort 
gibt es drei Arten von Objektlebensdauer, nämlich statisch, automatisch 
und dynamisch.
Statische Objekte existieren über die gesamte Programmlebensdauer, 
automatische Objekte bis zum Ende des Blocks, in dem sie definiert sind 
und dynamische so lange, bis man sie manuell wieder freigibt. Stack und 
Heap sind nun zwei Bereiche im Speicher. Ein weiterer ist das schon 
erwähnte BSS- und DATA-Segment. Üblicherweise werden die automatischen 
Objekte auf den Stack oder in Register gelegt und die dynamischen auf 
den Heap. Die statischen Objekte landen je nach Initialisierungswert in 
BSS oder DATA.

von Julius (Gast)


Lesenswert?

Im Heap wir der Speicher allokiert der mit malloc() angefordert wird. 
Dort wird also der dynamische Speicher angelegt. Das wurde bereits 
erwähnt. Auf den Stack landen z.B. Rücksprungadressen und werden Inhalte 
des Status Registers gesichert wenn eine Funktion aufgerufen oder ein 
Interrupt ausgelöst wird.

von Walter L. (charly2)


Lesenswert?

Der Stack ist ein Hardwarepointer im Prozessor und zeigt immer ins RAM. 
Jeder Prozessor hat min. einen. Er tritt z.B. bei IRQs in Aktion und 
speichert u.a. die Rücksprungadr. , damit am Ende der IRQSUB an der 
alten Stelle im Prog. weiter gemacht wird.
Was der Heap ist und was er macht und wozu er gut ist habe ich auch nie 
verstanden.

von Julius (Gast)


Lesenswert?

Über den Stack können aber auch Funktionen Rückgabewert an den 
Aufrufenden zurückgeben. Oder es können der aufgerufen Funktion 
Parameter übergeben werden. Das hängt aber alles von der Implementation 
des Compilers ab. Gerade für Rückgabewerte ist es, je nach 
Plattform/Comppiler, auch üblich Register zu verwenden.

von 🐧 DPA 🐧 (Gast)


Lesenswert?

Das schöne am Stack ist, dass er nicht fragmentieren kann. Man packt 
Daten oben drauf, und nimmt sie in der selben Reihenfolge auch wieder 
davon weg.

Das ist grossartig um Daten, die während eines Funktionsaufrufs 
gebraucht werden, zu speichern, weil wenn Funktion A Funktion B aufruft, 
muss Funktion B erst mal fertig sein, bevor Funktion A weiter machen und 
fertig werden kann, Funktion B kann also die Daten, die sie während 
ihrer Ausführung braucht auf den Stack legen, und wenn sie fertig ist, 
wieder wegnehmen (oder das Funktion A überlassen). Funktion B kann dabei 
vorher auch noch etwas für Funktion A auf dem Stack lassen.

Nun gibt es aber halt Situationen, wo das nicht ausreicht. Konkret, wenn 
man grössere Datenmengen hat, die man nicht herumkopieren will, und man 
will die in Funktion B zurück geben, oder wenn sonstige Funktionen die 
Daten eventuell später noch brauchen, man die also nicht einfach vom 
Stack wegräumen oder darauf verschieben kann/will. Dafür braucht man 
dann eine Datenstruktur,  wo man die Daten nicht gleich wieder wegräumen 
muss, sondern man die in beliebiger Reihenfolge reservieren und 
freigeben kann. Dafür nutzt man dann den Heap.

Als Beispiel, wenn ich global eine Liste von Katzenbildern habe, und 
eine Funktion, die alle Anzeigt, dann kann ich in der Funktion, wo ich 
Katzenbilder erstelle & hinzufüge diese nicht auf den Stack legen, weil 
das ja noch da sein muss, wenn ich die Bilder später mit der anderen 
Funktion auflisten will. Die Parameter, die ich zum Erstellen der Bilder 
brauche (Katzenfarbe, Grösse, etc.), und alle temporären dafür 
angelegten Daten, brauche ich aber nur während dem Funktionsaufruf zum 
erstellen des Bildes, und nachher nicht mehr. Ich kann diese also 
einfach auf den Stack legen.

von Julius (Gast)


Lesenswert?

Im Normalfall muss man sich um die Verwaltung des Stacks/Stackinhalts 
nicht kümmern. Das sollte der Compiler allein können.

von Walter L. (charly2)


Lesenswert?

Ich kenne keinen Prozessor, der keinen Stack als Hardwarepointer imple. 
hat.

von 900ss (900ss)


Lesenswert?

Walter L. schrieb:
> Ich kenne keinen Prozessor, der keinen Stack als Hardwarepointer imple.
> hat.

Und was ist zum Beispiel mit einem Register welches "SP" heisst in 
manchen Prozessoren und auf den Stackbereich zeigt?

von (prx) A. K. (prx)


Lesenswert?

Walter L. schrieb:
> Ich kenne keinen Prozessor, der keinen Stack als Hardwarepointer imple.
> hat.

Es gibt kleine Mikrocontroller, deren Return-Stack nicht im RAM liegt, 
sondern in wenigen speziellen und nicht anderweitig sichtbaren 
Registern. Wird Rekursion nicht zugelassen oder nicht genutzt, ist ein 
Stack im RAM unnötig.

Andere Architekturen definieren per Hardware keinen Stack, sondern 
speichern die Return-Adresse in einem normalen sichtbaren Register. Es 
bleibt dann dem Programm überlassen, was es damit macht.

Bei den AVRs gibt es mindestens eine Programmier-Umgebung, die Return- 
und Daten-Stack trennt. Der Hardware-SP ist dann nur für Return-Adressen 
zuständig, nicht für diesem Thread gemeinte Daten.

Die Implementierung eines von C genutzten Stacks kann in solchen 
Umgebungen u.U. eine reine Konvention des ABI sein, u.U. ohne besonderem 
Support durch die Hardware auskommend.

: Bearbeitet durch User
von Walter L. (charly2)


Lesenswert?

Ist es richtig, dass wenn ich "Heap" benutzen will, ich per Softwaere 
selber tätig werden muss (malloc();)?

Oder, welche Standardprogramme in C (z. B. wie printf()) benutzen/legen 
einen Heap an?

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


Lesenswert?

Walter L. schrieb:
> Ist es richtig, dass wenn ich "Heap" benutzen will, ich per Softwaere
> selber tätig werden muss (malloc();)?

Ja, was auch immer für dich "die Software" ist. ;-)

> Oder, welche Standardprogramme in C (z. B. wie printf()) benutzen/legen
> einen Heap an?

printf() "legt" keinen "Heap an", aber übliche stdio-Implementierungen 
greifen ihrerseits selbst auf malloc() zurück, um Datenpuffer für die 
Ein-/Ausgabe zu bekommen.

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


Lesenswert?

(prx) A. K. schrieb:
> Bei den AVRs gibt es mindestens eine Programmier-Umgebung, die Return-
> und Daten-Stack trennt.

IAR, wenn ich mich recht entsinne.

von 900ss (900ss)


Lesenswert?

900ss D. schrieb:
> Walter L. schrieb:
>> Ich kenne keinen Prozessor, der keinen Stack als Hardwarepointer imple.
>> hat.
>
> Und was ist zum Beispiel mit einem Register welches "SP" heisst in
> manchen Prozessoren und auf den Stackbereich zeigt?

upps, ich habe mich verlesen. Statt "keinen" hatte ich "einen" Stack als 
Hardwarepointer hat :)

von Gerald K. (geku)


Lesenswert?

Walter L. schrieb:
> Was der Heap ist und was er macht und wozu er gut ist habe ich auch nie
> verstanden.

Der Heap ist ein spezieller Speicherbereich im RAM. Aus diesen kann man 
sich mit Hilfe der Funktion malloc() Speicher in verschiedener Größe 
ausborgen und ihn, wenn nicht mehr benötigt, mit der Funktion free() 
zurück geben.
1
ptr  = malloc(size);
2
.....
3
......
4
free(ptr);

Da nicht nur ein  Speicher "gleichzeitig" ausgeborgt und freigegeben 
werden kann, kann sich der Heap fragmentieren.  Das heißt, freier und 
benutzer Speicher sind nicht sauber voneinander getrennt, sondern freie 
und benutzte Stellen unterschiedlicher Größe wechseln sich am Heap ab, 
der Heap wird löchrig. Wenn keine speziellen Maßnahmen getroffen werden, 
nimmt die Größe des zusammen hängenden freien Speichers ab. Die 
Konsequenz, mit der Zeit nimmt die maximale Größe des, mit malloc(), 
ausborgbaren Speichers ab. Malloc liefer dann als Speicheradresse NULL 
zurück.
Daher war die Benutzung des Heaps lange Zeit für sicherheitsrelevante 
Anwendungen  nicht zulässig.

: Bearbeitet durch User
von Georg (Gast)


Lesenswert?

Gerald K. schrieb:
> Daher war die Benutzung des Heaps lange Zeit für sicherheitsrelevante
> Anwendungen  nicht zulässig.

Dem steht aber die Philosophie fanatischer C-Jünger entgegen, JEDE 
Variable mit new anzulegen - Datensegmente müssen beim Programmstart 
leer sein.

Georg

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


Lesenswert?

Georg schrieb:
> Dem steht aber die Philosophie fanatischer C-Jünger entgegen, JEDE
> Variable mit new anzulegen

Wo hast du solche Leute denn je gesehen? Die sind mir noch nicht einmal 
theoretisch begegnet, praktisch gleich gar nicht.

von Nop (Gast)


Lesenswert?

Georg schrieb:

> Dem steht aber die Philosophie fanatischer C-Jünger entgegen, JEDE
> Variable mit new anzulegen

Wohl kaum, denn "new" ist kein Schlüsselwort in C.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Nop schrieb:
> Georg schrieb:
>
>> Dem steht aber die Philosophie fanatischer C-Jünger entgegen, JEDE
>> Variable mit new anzulegen
>
> Wohl kaum, denn "new" ist kein Schlüsselwort in C.

Und selbst in C++ kommt die direkte Verwendung von new ziemlich aus der
Mode.

von Rolf M. (rmagnus)


Lesenswert?

🐧 DPA 🐧 schrieb:
> Das schöne am Stack ist, dass er nicht fragmentieren kann. Man packt
> Daten oben drauf, und nimmt sie in der selben Reihenfolge auch wieder
> davon weg.

Das wäre ein Ringpuffer. Bei einem Stack nimmt man die Daten in 
umgekehrter Reihenfolge wieder weg (FIFO- vs. LIFO-Prinzip).

Yalu X. schrieb:
> Und selbst in C++ kommt die direkte Verwendung von new ziemlich aus der
> Mode.

Ob man es direkt oder indirekt verwendet, macht aber keinen Unterschied 
dafür, wo die Daten nachher liegen.

: Bearbeitet durch User
von cppbert3 (Gast)


Lesenswert?

Walter L. schrieb:
> Was der Heap ist und was er macht und wozu er gut ist habe ich auch nie
> verstanden.

Beispiele:

du musst 100 a 10MB Dateien parallel laden, dafür musst du den 
Speicherplatz dynamisch auf dem Heap reservieren, das passt niemals in 
den Stack

du hast sehr viele Daten die kaum eine statische Größe Aufweisen - d.h. 
Anzahl ob es 0,10,1000 oder mehr Elemente sind weißt du nicht im voraus, 
dafür kannst du auch keinen Stack verwenden - die theoretische Menge an 
möglichen Daten überschreitet z.B. permanent deine maximale Stackgröße 
und die Daten lasse sich nicht stückchenweise verarbeiten

und,und,und

die 4 oder mehr GB in den heutigen Rechnern sind nicht nur dazu da 
besonders viele Programme gleichzeitig zu laden - was denkst du warum 
ein Programm 50, 100 oder 200MB Speicher braucht? der Stack ist fix fuer 
deine Threads oder main, der Rest ist auf dem Heap, das ist bei 98% der 
nicht ultra-klein-Embedded Programmen der Normalfall

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


Lesenswert?

cppbert3 schrieb:
> das passt niemals in den Stack

Kannst du so nicht sagen, hängt von der Plattform ab.

Und nein, der Stack ist nicht "fix". Auf kleineren Plattformen wachsen 
oft Stack und Heap gegeneinander, es ist also recht egal, wer von den 
beiden den Speicher nimmt. Wenn er alle ist, hört's auf.

Auf größeren Plattformen werden so lange neue Speicherseiten vom OS 
angefordert, bis ein bestimmtes (meist konfigurierbares) Maximum 
erreicht ist. Das trifft sowohl für Stack als auch Heap zu.

von Rolf M. (rmagnus)


Lesenswert?

Jörg W. schrieb:
> cppbert3 schrieb:
>> das passt niemals in den Stack
>
> Kannst du so nicht sagen, hängt von der Plattform ab.

Wollte ich auch schon schreiben. Die Stack-Größe hat ja (abgesehen von 
der Gesamt-Größe des RAM) keine technische Limitierung, sondern wurde 
von jemandem so gewählt. Die kann also nicht Grund dafür sein, warum man 
einen Heap braucht.

von cppbert3 (Gast)


Lesenswert?

Jörg W. schrieb:
> cppbert3 schrieb:
>> das passt niemals in den Stack
>
> Kannst du so nicht sagen, hängt von der Plattform ab.

> Und nein, der Stack ist nicht "fix". Auf kleineren Plattformen wachsen
> oft Stack und Heap gegeneinander, es ist also recht egal, wer von den
> beiden den Speicher nimmt. Wenn er alle ist, hört's auf.

sorry, ich bezog mich auf x86

> Auf größeren Plattformen werden so lange neue Speicherseiten vom OS
> angefordert, bis ein bestimmtes (meist konfigurierbares) Maximum
> erreicht ist. Das trifft sowohl für Stack als auch Heap zu.

der Stack wächst nicht dynamisch - dann wäre sein sinn als schneller 
Zwischenspeicher völlig dahin - oder was meinst du?

von cppbert3 (Gast)


Lesenswert?

Rolf M. schrieb:
> Jörg W. schrieb:
>> cppbert3 schrieb:
>>> das passt niemals in den Stack
>>
>> Kannst du so nicht sagen, hängt von der Plattform ab.
>
> Wollte ich auch schon schreiben. Die Stack-Größe hat ja (abgesehen von
> der Gesamt-Größe des RAM) keine technische Limitierung, sondern wurde
> von jemandem so gewählt. Die kann also nicht Grund dafür sein, warum man
> einen Heap braucht.

der Stack wird aber z.B. unter Windows/Linux pro Thread und Prozess zum 
Startzeitpunkt vor-definiert z.B. auf 2 oder 5MB, daran kannst du nicht 
rütteln sonst müssten man ja irgenein dynamisches wachstum erlaube was 
den Stack wieder (im Vergleich zu einer statischen Größe - d.h. ohne 
jegliches überwachendes Management) wieder zu langsam machen würde

von (prx) A. K. (prx)


Lesenswert?

cppbert3 schrieb:
> der Stack wächst nicht dynamisch - dann wäre sein sinn als schneller
> Zwischenspeicher völlig dahin

Ich kenne das mit wählbarer Maximalgröße. Damit endlose Rekursion nicht 
den Speicher des Systems platzen lässt.

Stack wird bei Systemen mit virtuellen Speicher automatisch und 
bedarfsgesteuert bis zu diesem Wert vergrößert. Vorher ist das nur 
Adressraum, ohne RAM dahinter.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

cppbert3 schrieb:
>> Auf größeren Plattformen werden so lange neue Speicherseiten vom OS
>> angefordert, bis ein bestimmtes (meist konfigurierbares) Maximum
>> erreicht ist. Das trifft sowohl für Stack als auch Heap zu.
>
> der Stack wächst nicht dynamisch - dann wäre sein sinn als schneller
> Zwischenspeicher völlig dahin - oder was meinst du?

Der Stack selbst wächst schon dynamisch, sonst bräuchte man keinen 
Stackpointer, der auf das Ende zeigt. Ob der für den Stack reservierte 
Speicher (also die Maximalgröße für den Stack) auch dynamisch wachsen 
kann, hängt vom System ab. Unter Linux wächst diese Größe nicht von 
selbst, aber man kann sie per setrlimit ändern.

von cppbert3 (Gast)


Lesenswert?

Rolf M. schrieb:
> Der Stack selbst wächst schon dynamisch, sonst bräuchte man keinen
> Stackpointer

ich meinte der Stack vergroessert sich nicht wenn man an seine Grenze 
stosst

von cppbert3 (Gast)


Lesenswert?

(prx) A. K. schrieb:
> Stack wird bei Systemen mit virtuellen Speicher automatisch und
> bedarfsgesteuert bis zu diesem Wert vergrößert. Vorher ist das nur
> Adressraum, ohne RAM dahinter.

wie gesagt die Stackgröße ist fix - und das vergrößern mit dem 
virtuellen Speicher läuft in Hardware? in Software wäre das doch viel zu 
langsam

deswegen - wenn ich Daten habe die ich komplett im "Speicher" brauche 
sollte ich das nicht unbedingt versuchen in den Stack zu stopfen - wenn 
z.B. die Daten wissentlich die Stackgröße locker erreichen

auch alloca ist da nicht ganz ungefährlich :)

von cppbert3 (Gast)


Lesenswert?

Rolf M. schrieb:
> Ob der für den Stack reservierte
> Speicher (also die Maximalgröße für den Stack) auch dynamisch wachsen
> kann, hängt vom System ab.

kannst du mir ein System nennen das dynamisches Wachstum des Stack 
erlaubt?
also das man ein min Größe angibt und dann der Stack so lange wachsen 
kann bis der RAM verbraucht ist

das muss doch höchst langsam sein - wird da jedes mal im Code geprüft ob 
man an der Grenze ist oder jedes mal ein Page-Check gemacht, 100Mio 
mal...?

von Rolf M. (rmagnus)


Lesenswert?

cppbert3 schrieb:
> Rolf M. schrieb:
>> Ob der für den Stack reservierte
>> Speicher (also die Maximalgröße für den Stack) auch dynamisch wachsen
>> kann, hängt vom System ab.
>
> kannst du mir ein System nennen das dynamisches Wachstum des Stack
> erlaubt?
> also das man ein min Größe angibt und dann der Stack so lange wachsen
> kann bis der RAM verbraucht ist

AVR? Der hat hardwaretechnisch gar nicht die Möglichkeit, das zu 
begrenzen.

> das muss doch höchst langsam sein - wird da jedes mal im Code geprüft ob
> man an der Grenze ist oder jedes mal ein Page-Check gemacht, 100Mio
> mal...?

Wieso "page-check"? Eine MMU checkt sowas von sich aus. Unter Linux 
gibt's z.B. einen Segmentation Fault, wenn das Maximum überschritten 
wird. Stattdessen könnte man im entsprechenden Handler genauso den 
Bereich einfach vergrößern. Da muss in Software nix dafür gecheckt 
werden. Auslagerung funktioniert ja auch genau auf diese Weise, und die 
Lazy Allocation beim Heap ebenfalls.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

cppbert3 schrieb:
> wie gesagt die Stackgröße ist fix - und das vergrößern mit dem
> virtuellen Speicher läuft in Hardware

Wenn das System so wie beschrieben arbeitet, dann führt ein Zugriff auf 
eine Speicherseite im Stack, hinter der bisher kein RAM steht, 
automatisch dazu, dass ihr RAM zugeordnet wird. Voraussetzung ist dann 
nur, dass der Adressraum des Stacks ausreichend gross definiert wurde.

cppbert3 schrieb:
> das muss doch höchst langsam sein

Virtueller Speicher ist eine Eigenschaft der MMU des Prozessors. Solche 
Kontrollen finden per Hardware bei jedem einzelnen Speicherzugriff 
statt, egal wohin. Zugriff auf nicht definierten Adressraum führt i.d.R. 
zum Abbruch des Programms. Geschieht das nicht in einer Anwendung, 
sondern im Kernel oder einem Treiber des Betriebssystem, führt das zum 
Bluescreen oder Ṕanic.

: Bearbeitet durch User
von (prx) A. K. (prx)


Lesenswert?

cppbert3 schrieb:
> also das man ein min Größe angibt und dann der Stack so lange wachsen
> kann bis der RAM verbraucht ist

Andersrum. Man gibt eine maximale Grösse an, aber es wird dafür nur 
Adressraum reserviert, kein RAM. Bei 64-Bit Programmen hat man 
ausreichend Gesamtadressraum um grosszügig zu sein.

Dass man nicht immer sehr grosse Stacks reserviert ist eine 
Schutzmassnahme. Meist sind genutzte Stacks in GB Grösse nicht Absicht, 
sondern Amoklauf.

: Bearbeitet durch User
von cppbert3 (Gast)


Lesenswert?

Rolf M. schrieb:
> AVR? Der hat hardwaretechnisch gar nicht die Möglichkeit, das zu
> begrenzen.

wie begrenzt denn x86 das hardwaretechnisch? dort kann man es nur 
erkennen ob es passiert, aber nicht wirklich "begrenzen"

Fakt ist: Es gibt bei den meisten Systemen einen(oder mehrere) begrenzte 
Stacks die meistens eine Maximalgröße Aufweisen - ob das intern durch 
virtuellen Memory oder sonstwen gehandhabt wird war mir bei meine 
Aussage egal

das der Stack sich dynamisch füllt(aka wächst) ist klar, aber das der 
Stack-Max sich automatisch vergrößert ist denke ich selten

von (prx) A. K. (prx)


Lesenswert?

cppbert3 schrieb:
> wie begrenzt denn x86 das hardwaretechnisch?

https://wiki.osdev.org/Paging

von (prx) A. K. (prx)


Lesenswert?

cppbert3 schrieb:
> das der Stack sich dynamisch füllt(aka wächst) ist klar, aber das der
> Stack-Max sich automatisch vergrößert ist denke ich selten

Das interpretierst du in die Antworten rein, steht dort aber nicht drin.

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


Lesenswert?

cppbert3 schrieb:
> Fakt ist: Es gibt bei den meisten Systemen einen(oder mehrere) begrenzte
> Stacks die meistens eine Maximalgröße Aufweisen

Du kannst mit setrlimit sowohl für den Stack als auch für den Heap 
(Datensegment) nahezu beliebige Ressource Limits einstellen.

Davon allein wird dort nichts alloziert. Alloziert wird erst, wenn es 
gebraucht wird (via page fault, wie A.K. beschrieben hat).

Insofern verhalten sich bei einem VM-System diese beiden Bereiche 
komplett gleich mit der einzigen Ausnahme, dass das Datensegment von 
unten nach oben wächst, der Stack von oben nach unten.

Das bezieht sich auf Prozess-Stacks. Mit Thread-Stacks verhält sich das 
u.U. anders, je nachdem, wie der Thread implementiert ist. Ist er (wie 
bei vielen Unixen) wie ein Prozess implementiert, kann man das dort 
natürlich genauso handhaben, ansonsten muss das Thread-System explizit 
vorab Speicher allozieren (dann wohl auf dem Heap).

von (prx) A. K. (prx)


Lesenswert?

Jörg W. schrieb:
> ansonsten muss das Thread-System explizit
> vorab Speicher allozieren (dann wohl auf dem Heap).

Wenn der Allokator im Heap diesem Bereich vorsorglich komplett mit 
Nullen plättet, ist das RAM da, finito. Wer klug genug ist, das nicht zu 
tun, sondern es dem OS überlässt, kriegt erst einmal nur Adressraum, 
plus evtl. ein paar reale Bytes vom Heap-Management vorne und hinten. 
Auch allozierter aber nicht genutzter Heap kann locker ohne reale 
RAM-Zuordnung leben. Bis zum Zugriff, Seite für Seite.

: Bearbeitet durch User
von cppbert3 (Gast)


Lesenswert?

(prx) A. K. schrieb:
> cppbert3 schrieb:
>> wie begrenzt denn x86 das hardwaretechnisch?
>
> https://wiki.osdev.org/Paging

das ist klar - Begrenzen kann man auch gar nicht - sonst gäbe es ja gar 
keine Stackoverflows :) - man kann eben sauber erkennen das es passiert

und AVR hat das Feature eben nicht, oder?

Beitrag #6691286 wurde vom Autor gelöscht.
von Yalu X. (yalu) (Moderator)


Lesenswert?

Mit setrlimit (oder ulimit in der Shell) kann man die Limitierung der
Stackgröße auch komplett aufheben. Dann hat man quasi einen voll
dynamischen Stack, der nur durch die Größe des physikalischen Speichers
begrenzt wird. Ist dieser aufgebraucht, kommt der OOM-Killer und tötet
den seiner Ansicht nach bösesten oder unwichtigsten Prozess, um wieder
Speicher frei zu machen

von Rolf M. (rmagnus)


Lesenswert?

cppbert3 schrieb:
> Rolf M. schrieb:
>> AVR? Der hat hardwaretechnisch gar nicht die Möglichkeit, das zu
>> begrenzen.
>
> wie begrenzt denn x86 das hardwaretechnisch?

Indem vor einem Überschreiten der Grenze im Prozessor eine 
Page-Fault-Exception ausgelöst wird und das Betriebssystem als Reaktion 
darauf den Prozess terminiert. Natürlich nur, wenn das Betriebssystem 
die MMU vorher passend konfiguriert hat.

> dort kann man es nur erkennen ob es passiert, aber nicht wirklich
> "begrenzen"

Wenn man das nicht könnte, könnte ein Stacküberlauf eines Prozesses das 
ganze Betriebssystem zum Absturz bringen.

> Fakt ist: Es gibt bei den meisten Systemen einen(oder mehrere) begrenzte
> Stacks die meistens eine Maximalgröße Aufweisen - ob das intern durch
> virtuellen Memory oder sonstwen gehandhabt wird war mir bei meine
> Aussage egal

Es ist eigentlich umgekehrt: Im einfachsten Fall gibt es keine 
Maximalgröße. Der Stack wächst einfach weiter, ggf. auch bis er andere 
Daten im RAM gnadenlos überschreibt. Bei kleinen Prozessoren wie AVR ist 
es nicht unüblich, dass Stack und Heap auf einander zu wachsen. Dann 
gibt es kein fixes Maximum. Es hängt einfach davon ab, wie viel Heap man 
schon benutzt. Der Prozessor kann eine Überschreitung aber sowieso nicht 
in Hardware prüfen oder darauf reagieren. Hat man dagegen eine MPU oder 
MMU, kann die genutzt werden, um eine Grenze einzustellen, auf deren 
Überschreiten man auch reagieren kann, ohne dass das bei jedem Zugriff 
erst softwareseitig geprüft werden müssten.

von Experte (Gast)


Lesenswert?

cppbert3 schrieb:
> sonst gäbe es ja gar
> keine Stackoverflows

Ein Stackoverflow ist was ganz anderes:

Man spricht von einem Stackoverflow, wenn der von einer Funktion auf dem 
Stack reservierte Speicherbereich für ihre lokalen Daten (Variablen) 
durch ein Programmfehler unerwartet überschritten wird.

Problematisch an dieser Geschichte ist (bzw. war), dass auf dem Stack in 
der Regel Daten und die Rücksprungadresse dicht beieinander liegen. Dann 
konnte mitunter durch ein Stackoverflow boshafter Code auf den Stack 
abgelegt werden, und die Rücksprungadresse der Funktion manipuliert 
werden. Führt daraufhin die Funktionen einen Rücksprung aus, wird statt 
zu der ursprünglich aufrufenden Funktion in den boshaften Code 
gesprungen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Experte schrieb:
> Ein Stackoverflow ist was ganz anderes:
>
> Man spricht von einem Stackoverflow, wenn der von einer Funktion auf dem
> Stack reservierte Speicherbereich für ihre lokalen Daten (Variablen)
> durch ein Programmfehler unerwartet überschritten wird.

Nein, das ist ein Buffer Overflow. Da überläuft nicht der Stack, sondern 
nur eine Variable, typischerweise ein Array.

von cppbert3 (Gast)


Lesenswert?

Rolf M. schrieb:
> Indem vor einem Überschreiten der Grenze im Prozessor eine
> Page-Fault-Exception ausgelöst wird und das Betriebssystem als Reaktion
> darauf den Prozess terminiert. Natürlich nur, wenn das Betriebssystem
> die MMU vorher passend konfiguriert hat.

Genau das meinte ich mit "erkennen", mir ist klar wie das realisiert 
wird

von Georg (Gast)


Lesenswert?

cppbert3 schrieb:
> und AVR hat das Feature eben nicht, oder?

Muss er ja auch nicht, man kann den Stack auch ohne spezielle 
Hardwarefunktion überwachen, per Software.

Mein Pascal-Compiler für Z80 aus dem vorigen Jahrtausend hatte die 
Option zu überwachen, ob sich der von unten wachsende Heap und der von 
oben wachsende Stack (das übliche Memory-Modell) überschneiden. Ich kann 
nicht so recht glauben dass damals die Compiler mehr konnten als heute, 
aber wenn man hier so mitliest...

Georg

von cppbert3 (Gast)


Lesenswert?

Georg schrieb:
> Muss er ja auch nicht, man kann den Stack auch ohne spezielle
> Hardwarefunktion überwachen, per Software.

Das geht heute auch noch mit Abstrichen, z.B. in Form von intensiveren 
Runtime Test bei Stacknutzung um Fehler aufzudecken, ist aber eben alles 
sehr sehr langsam

Beitrag #6691717 wurde von einem Moderator gelöscht.
von c-hater (Gast)


Lesenswert?

cppbert3 schrieb:

> das ist klar - Begrenzen kann man auch gar nicht - sonst gäbe es ja gar
> keine Stackoverflows :) - man kann eben sauber erkennen das es passiert
>
> und AVR hat das Feature eben nicht, oder?

Nicht in Hardware, aber man kann es in die Software einbauen. Kostet 
natürlich Rechenzeit und zwar nicht ganz unerheblich.

Das Prinzip ist simpel: Du legst irgendwo im Speicher eine Variable an, 
in der die höchste benutzte statische Speicheradresse abgelegt wird oder 
(bei Verwendung dynamischen Speichers) halt der heap top.

Und dann muss in jeder Funktion (insbesondere auch in jeder ISR) 
kontrolliert werden, dass der aktuelle SP halt noch größer ist als der 
Inhalt dieser Variablen.

Blöderweise ist dazu ein 16Bit-Vergleich nötig. Das bedeutet 
(insbesondere für ISRs): es sind zwingend mindestens 2 Register zu 
sichern und auch das Flag-Register. Je nachdem, was die ISR sonst noch 
tut, kann das einen erheblichen Mehraufwand bedeuten.

Ich würd's nicht machen. Denn eigentlich ist (mit zwei Ausnahmen) der 
maximale Stackbedarf zur Designzeit ermittelbar. Die zwei Ausnahmen sind 
auf Grund äußerer Daten berechnete Funktionszeiger und rekursive 
Unterprogramme. Man vermeidet einfach die Verwendung solcher Konstrukte 
und gut isses.

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.