Perché lo stack di chiamate non è implementato come array dinamico nel SO moderno? [duplicare]

1

Quando un sistema operativo moderno carica un processo, pre-alloca una certa quantità di spazio per lo stack. Ciò significa che il programmatore deve fare attenzione a evitare l'overflow dello stack limitando la profondità della chiamata e / o aumentando le dimensioni dello stack in fase di esecuzione.

A prima vista, questo sembra inutilmente complicato. Perché il sistema operativo non implementa lo stack di chiamate utilizzando un array dinamico? Quando viene superata la dimensione iniziale (modesta) dell'array, la matrice può essere raddoppiata e riallocata in una nuova posizione sullo heap, garantendo un costo O (1) ammortizzato di push . In questo modo, il programmatore non dovrà preoccuparsi delle dimensioni dello stack (beh, non più di quanto non debba preoccuparsi della memoria allocata nello heap).

Nell'argomento sopra ho assunto un sistema operativo in modalità reale; ma in realtà tutti i sistemi operativi moderni hanno memoria virtuale. Questo sembra rafforzare solo l'argomentazione per lo stack di dimensioni dinamiche: c'è una memoria virtuale essenzialmente illimitata disponibile per un processo, quindi perché non allocare, ad esempio, un quarto dello spazio del processo come stack? Poiché il processo ha bisogno di più memoria, colpirà le nuove pagine nello spazio virtuale, che genererà automaticamente il mapping alla memoria fisica.

    
posta max 01.11.2016 - 08:33
fonte

3 risposte

4

Le dimensioni predefinite dello stack di thread sui moderni SO desktop si trovano di solito nell'intervallo MB. Utilizzeranno il paging della memoria virtuale mentre descrivi di utilizzare solo la memoria fisica, se necessario.

Il motivo principale per cui non è possibile rendere dinamicamente fattibile è perché è raramente necessario. In pratica, la maggior parte delle volte che un thread fa saltare il limite dello stack è una sorta di problema di ricorsione. In quel caso avrai bisogno che smetta di crescere ad un certo punto, quindi avrai bisogno di un limite superiore. Potresti sentire che il limite dovrebbe essere molto più ampio ma non è la vista della maggior parte degli implementatori del sistema operativo.

O per mettere in un altro modo, gli implementatori scelgono una dimensione che è abbastanza grande nella maggior parte dei casi ma non è così grande da causare troppi problemi quando c'è un problema.

BTW un commento in un'altra risposta menzionata golang . Implementa qualcosa come descrivi per le goroutine (thread molto leggeri). Il motivo è, penso, istruttivo.

golang è progettato per funzionare con migliaia di goroutine in esecuzione simultaneamente. In quanto tale, viene eseguito con una allocazione di memoria molto più piccola per thread rispetto a quella che si vedrebbe per un thread del sistema operativo. Credo che 4k sia l'impostazione predefinita. Ora questo è molto piccolo e non vorrai aumentare tutte le goroutine se tu avessi un piccolo numero che necessitava di uno stack più grande. Da qui il dinamismo.

Naturalmente, questo è molto diverso dal tuo tipico utilizzo del modello di thread del sistema operativo, quindi il diverso approccio adottato.

    
risposta data 01.11.2016 - 18:03
fonte
4

I programmi compilati per codice macchina con i compilatori di oggi presumono che lo stack sia contiguo (e non si muova durante l'esecuzione), quindi questo interromperà la compatibilità con quasi tutti i software esistenti.

    
risposta data 01.11.2016 - 08:57
fonte
2

Stiamo parlando di livello di processore, non di livello di libreria di classi. È come chiedere perché i mattoni non hanno ascensori negli edifici moderni.

Lo stack di chiamate è primitivo, non più di bit in un registro nel processore che costituisce un indirizzo che punta a una posizione di memoria. Gli array non hanno significato qui, non è un oggetto. Non sarebbe così bello se fosse o perché viene usato così spesso che il minimo sovraccarico (come il controllo dei limiti) danneggerebbe enormemente le prestazioni.

Il processore ha hardware che monitora i limiti. Se il puntatore dello stack esce dal range consentito, parte un circuito dedicato che reindirizza l'esecuzione del programma in un'altra posizione in cui si trova il gestore trap. Tuttavia, questa non è la normale logica di programmazione, non troverai alcun C # che controlli se corrente > Max.

    
risposta data 01.11.2016 - 16:54
fonte

Leggi altre domande sui tag