Warum wachsen Stacks typischerweise nach unten?

Ich weiß, dass in den Architekturen, mit denen ich persönlich vertraut bin (x86, 6502 usw.), der Stack normalerweise nach unten wächst (dh jedes auf den Stack gedrückte Element führt zu einem dekrementierten SP, nicht zu einem inkrementierten).

Ich wundere mich über die historische Begründung dafür. Ich weiß, dass es in einem einheitlichen Adressraum bequem ist, den Stapel am entgegengesetzten Ende des Datensegments zu starten (sagen wir mal), so dass es nur ein Problem gibt, wenn die zwei Seiten in der Mitte kollidieren. Aber warum bekommt der Stapel traditionell den oberen Teil? Vor allem, wenn dies das Gegenteil des “konzeptionellen” Modells ist?

(Und beachten Sie, dass der Stack in der 6502-Architektur auch nach unten wächst, obwohl er auf eine einzelne 256-Byte-Seite beschränkt ist, und diese Richtungswahl scheint willkürlich zu sein.)

   

Was die historische Begründung betrifft, kann ich nicht mit Sicherheit sagen (weil ich sie nicht entworfen habe). Meine Gedanken zu diesem Thema sind, dass frühe CPUs ihren ursprünglichen Programmzähler auf 0 gesetzt haben und es ein natürlicher Wunsch war, den Stack am anderen Ende zu starten und nach unten zu wachsen, da ihr Code natürlich nach oben wächst.

Nebenbei bemerkt, ist diese Einstellung des Programmzählers auf 0 beim Zurücksetzen nicht für alle frühen CPUs der Fall. Zum Beispiel würde das Motorola 6809 den Programmzähler von den Adressen 0xfffe/f holen, so dass Sie beginnen könnten, an einem beliebigen Ort zu laufen, abhängig davon, was an dieser Adresse geliefert wurde (normalerweise, aber nicht beschränkt auf, ROM).

Eines der ersten Dinge, die einige historische Systeme tun würden, wäre, Speicher von oben zu durchsuchen, bis er einen Speicherort gefunden hat, der den gleichen geschriebenen Wert zurücklesen würde, so dass er den tatsächlich installierten RAM kennen würde (z. B. ein z80 mit 64K Adressraum) hatte nicht notwendigerweise 64K oder RAM, tatsächlich wären 64K in meinen frühen Tagen massiv gewesen ). Sobald es die oberste tatsächliche Adresse gefunden hat, würde es den Stapelzeiger entsprechend einstellen und könnte dann mit dem Aufruf von Unterprogrammen beginnen. Diese Abtastung würde im Allgemeinen durch den CPU-laufenden Code im ROM als Teil des Starts durchgeführt werden.

Im Hinblick auf das Wachstum der Stacks wachsen nicht alle nach unten, siehe diese Antwort für Details.

Eine gute Erklärung, die ich hörte, war, dass einige Maschinen in der Vergangenheit nur unsignierte Offsets haben konnten, also sollten Sie den Stack nach unten wachsen lassen, damit Sie Ihre Einheimischen treffen können, ohne die zusätzliche statement zu verlieren, einen negativen Offset vorzutäuschen.

Ein möglicher Grund könnte sein, dass es die Ausrichtung vereinfacht. Wenn Sie eine lokale Variable auf dem Stapel platzieren, die auf einer 4-Byte-Grenze platziert werden muss, können Sie einfach die Größe des Objekts vom Stapelzeiger subtrahieren und dann die beiden unteren Bits auf Null setzen, um eine korrekt ausgerichtete Adresse zu erhalten. Wenn der Stapel nach oben wächst, wird die Ausrichtung etwas schwieriger.

IIRC der Stapel wächst nach unten, weil der Haufen nach oben wächst. Es hätte andersherum sein können.

Ich glaube, es ist eine reine Designentscheidung. Nicht alle von ihnen wachsen nach unten – siehe diesen SO-Thread für eine gute Diskussion über die Richtung des Stack-Wachstums auf verschiedenen Architekturen.

Ich glaube, die Konvention begann mit der IBM 704 und ihrem berüchtigten Dekrement-Register. Die moderne Sprache würde es ein Offset-Feld der statement nennen, aber der Punkt ist, dass sie nach unten gegangen sind , nicht nach oben .

Ich bin mir nicht sicher, aber ich habe damals einige Programme für die VAX / VMS programmiert. Ich scheine mich zu erinnern, dass ein Teil der Erinnerung (der Haufen ??) nach oben geht und der Stapel nach unten geht. Als die beiden sich trafen, warst du aus dem Gedächtnis.

Nur 2c mehr:

Abgesehen von allen erwähnten historischen Gründen bin ich mir ziemlich sicher, dass es keinen Grund gibt, der in modernen processoren gültig ist. Alle processoren können vorzeichenbehaftete Offsets nehmen, und die Maximierung der Heap / Stack-Distanz ist ziemlich egal, seit wir uns mit mehreren Threads beschäftigt haben.

Ich persönlich halte dies für einen Sicherheitserrors. Wenn die Entwickler der x64-Architektur beispielsweise die Stapelwachstumsrichtung umgekehrt hätten, wären Stapelpufferüberläufe eliminiert worden – was eine große Sache ist.