Perché gli stack in genere crescono verso il basso?


95

So che nelle architetture che conosco personalmente (x86, 6502, ecc.), Lo stack cresce tipicamente verso il basso (cioè ogni elemento inserito nello stack risulta in un SP decrementato, non incrementato).

Mi chiedo quale sia la logica storica di questo. So che in uno spazio di indirizzi unificato, è conveniente avviare lo stack all'estremità opposta del segmento di dati (diciamo) quindi c'è solo un problema se i due lati si scontrano nel mezzo. Ma perché lo stack tradizionalmente ottiene la parte superiore? Soprattutto visto come questo sia l'opposto del modello "concettuale"?

(E si noti che nell'architettura 6502, lo stack cresce anche verso il basso, anche se è limitato a una singola pagina da 256 byte, e questa scelta di direzione sembra arbitraria.)

Risposte:


52

Quanto alla logica storica, non posso dirlo con certezza (perché non li ho progettati io). I miei pensieri in merito sono che le prime CPU avevano il loro contatore di programma originale impostato su 0 ed era un desiderio naturale di avviare lo stack dall'altra parte e crescere verso il basso, poiché il loro codice cresce naturalmente verso l'alto.

Per inciso, si noti che questa impostazione del contatore del programma su 0 al ripristino non è il caso per tutte le prime CPU. Ad esempio, il Motorola 6809 recupera il contatore del programma dagli indirizzi in 0xfffe/fmodo da poter iniziare a funzionare in una posizione arbitraria, a seconda di ciò che è stato fornito a quell'indirizzo (di solito, ma non limitato a, ROM).

Una delle prime cose che alcuni sistemi storici farebbero sarebbe quella di scansionare la memoria dall'alto fino a trovare una posizione che leggesse lo stesso valore scritto, in modo che conoscesse la RAM effettiva installata (ad esempio, uno z80 con 64K di spazio degli indirizzi non aveva necessariamente 64K o RAM, infatti 64K sarebbe stato enorme nei miei primi giorni). Una volta trovato l'indirizzo effettivo superiore, imposterà il puntatore dello stack in modo appropriato e potrebbe quindi iniziare a chiamare le subroutine. Questa scansione viene generalmente eseguita dalla CPU che esegue il codice nella ROM come parte dell'avvio.

Per quanto riguarda la crescita degli stack, non tutti crescono verso il basso, vedere questa risposta per i dettagli.


1
Mi piace la storia della strategia di rilevamento della RAM Z80. Ha un senso che i segmenti di testo siano disposti in modo crescente: i programmatori di un tempo avevano un contatto un po 'più diretto con la gestione delle implicazioni di ciò rispetto allo stack. Grazie paxdiablo. Anche il puntatore all'insieme di forme alternative di implementazioni dello stack è molto interessante.
Ben Zotto

La memoria dei primi giorni non ha un modo per notificare la sua dimensione e dobbiamo calcolarla manualmente?
phuclv

1
@ LưuVĩnhPhúc, devo presumere che tu sia una generazione (o due) dietro di me. Ricordo ancora il metodo TRS-80 modello 3 per ottenere la data e l'ora da chiedere all'utente all'avvio. Avere uno scanner di memoria per impostare il limite superiore della memoria era considerato lo stato dell'arte nel passato :-) Riesci a immaginare cosa accadrebbe se Windows chiedesse l'ora, o quanta memoria avevi, ogni volta che avvii il sistema?
paxdiablo

1
Infatti, la documentazione di Zilog Z80 dice che la parte si avvia impostando il registro del PC su 0000h ed eseguendo. Imposta la modalità di interruzione su 0, disabilita gli interrupt e imposta anche i registri I e R su 0. Dopodiché, inizia l'esecuzione. A 0000h, inizia l'esecuzione del codice. QUEL codice deve inizializzare il puntatore dello stack prima di poter chiamare una subroutine o abilitare gli interrupt. Quale venditore vende uno Z80 che si comporta nel modo in cui descrivi?
MikeB

1
Mike, scusa, avrei dovuto essere più chiaro. Quando ho detto che la memoria della CPU ha scansionato, non intendevo che fosse una caratteristica della CPU stessa. In realtà era controllato da un programma nella ROM. Chiarirò.
paxdiablo

21

Una buona spiegazione che ho sentito è che alcune macchine in passato potevano avere solo offset senza segno, quindi vorresti che lo stack crescesse verso il basso in modo da poter colpire i tuoi locali senza dover perdere le istruzioni extra per simulare un offset negativo.


7

Stanley Mazor (architetto 4004 e 8080) spiega come è stata scelta la direzione di crescita dello stack per 8080 (ed eventualmente per 8086) in "Microprocessori Intel: da 8008 a 8086" :

Il puntatore dello stack è stato scelto per funzionare "in discesa" (con lo stack che avanza verso una memoria inferiore) per semplificare l'indicizzazione nello stack dal programma dell'utente (indicizzazione positiva) e per semplificare la visualizzazione del contenuto dello stack da un pannello frontale.


6

Una possibile ragione potrebbe essere che semplifica l'allineamento. Se si posiziona una variabile locale sullo stack che deve essere posizionata su un limite di 4 byte, è possibile sottrarre semplicemente la dimensione dell'oggetto dal puntatore dello stack e quindi azzerare i due bit inferiori per ottenere un indirizzo correttamente allineato. Se la pila cresce verso l'alto, garantire l'allineamento diventa un po 'più complicato.


1
I computer non sottraggono; aggiungono il complimento di 2. Tutto ciò che viene fatto sottraendo è in realtà fatto aggiungendo. Considera, i computer hanno sommatori, non sottrattori.
jww

1
@jww - questa è una distinzione senza differenze. Potrei aver affermato che i computer non aggiungono ma si limitano a sottrarre! Ai fini di questa risposta, non ha molta importanza, ma la maggior parte delle ALU utilizzerà un circuito che supporta sia l'addizione che la sottrazione con le stesse prestazioni. Cioè, mentre A - Bpotrebbe essere implementato concettualmente come A + (-B)(cioè, un passaggio di negazione separato per B), non è in pratica.
BeeOnRope

@jww Il tuo pignolo è sbagliato per i primi computer: ci è voluto un po 'di tempo prima che il complemento a due vincesse, e fino a quando non lo ha fatto, c'erano computer che utilizzavano il proprio complemento e segno e grandezza e forse invece altre cose. Con queste implementazioni, potrebbe esserci stato un vantaggio nell'addizione rispetto alla sottrazione. Quindi, in assenza di informazioni aggiuntive, è sbagliato escluderlo come possibile fattore che influenza le scelte dello schema di indirizzamento come la direzione dello stack.
mtraceur

4

IIRC lo stack cresce verso il basso perché l'heap cresce verso l'alto. Potrebbe essere stato il contrario.


5
Un heap che cresce verso l'alto consente in alcuni casi una riallocazione efficiente, ma un heap verso il basso praticamente non lo fa mai.
Peter Cordes

@PeterCordes perché?
Yashas

3
@Yashas: perché ha realloc(3)bisogno di più spazio dopo un oggetto per estendere la mappatura senza copiare. La riallocazione ripetuta dello stesso oggetto è possibile quando è seguita da una quantità arbitraria di spazio inutilizzato.
Peter Cordes

2

Credo che sia puramente una decisione di design. Non tutti crescono verso il basso - vedi questo thread SO per alcune buone discussioni sulla direzione della crescita dello stack su diverse architetture.


1

Credo che la convenzione sia iniziata con l'IBM 704 e il suo famigerato "registro di decremento". Il linguaggio moderno lo chiamerebbe un campo di offset dell'istruzione, ma il punto è che sono andati giù , non su .


1

Solo 2c in più:

Al di là di tutte le ragioni storiche menzionate, sono abbastanza certo che non ci sia motivo valido nei processori moderni. Tutti i processori possono accettare offset con segno e massimizzare la distanza heap / stack è piuttosto discutibile da quando abbiamo iniziato a trattare con più thread.

Personalmente lo considero un difetto di progettazione della sicurezza. Se, ad esempio, i progettisti dell'architettura x64 avessero invertito la direzione di crescita dello stack, la maggior parte degli overflow del buffer dello stack sarebbe stata eliminata, il che è un grosso problema. (poiché le stringhe crescono verso l'alto).


0

Non ne sono sicuro, ma ho fatto un po 'di programmazione per VAX / VMS in passato. Mi sembra di ricordare una parte della memoria (il mucchio ??) che sale e lo stack che scende. Quando i due si sono incontrati, allora eri senza memoria.


1
Questo è vero, ma allora perché il mucchio cresce verso l'alto e non viceversa?
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

0

Un vantaggio della crescita dello stack decrescente in un sistema embedded minimo è che un singolo blocco di RAM può essere mappato in modo ridondante sia nella pagina O che nella pagina 1, consentendo di assegnare zero variabili di pagina a partire da 0x000 e lo stack che cresce verso il basso da 0x1FF, massimizzando il importo che dovrebbe aumentare prima di sovrascrivere le variabili.

Uno degli obiettivi di progettazione originali del 6502 era che potesse essere combinato, ad esempio, con un 6530, risultando in un sistema microcontrollore a due chip con 1 KB di ROM del programma, timer, I / O e 64 byte di RAM condivisi tra le variabili stack e page zero. In confronto, il sistema embedded minimo di quel tempo basato su un 8080 o 6800 sarebbe stato di quattro o cinque chip.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.