come viene assegnato lo stack e l'heap a ciascun processo?

0

Come i processi multipli sono archiviati nella memoria principale, capisco che ogni processo sarà diviso in pagine di uguale dimensione e sarà archiviato nei frame della memoria principale. se tutta la memoria principale è suddivisa nelle pagine, allora come sono le aree dello stack, l'area dell'heap, la sezione del codice data al processo?

La mia comprensione è che c'è un'area di stack e un'area heap comuni.

la prossima domanda è: - quando un metodo viene eseguito, viene allocato nell'area dello stack e un record di attivazione viene inserito nello stack.

Sto anche considerando qui il cambio di contesto. ad esempio:

---------------
| process: p1 |
---------------
fun(int a)
{
if (a=0)
return 0;

fun(a--);
}

main()
{
 fun(5);
}

---------------
| process: p2 |
---------------

NoFun(int a)
{
if (a=0);
return 0;

NoFun(a--);
}
main()
{
 fun(3)
}

ora i processi p1 e p2 saranno caricati nella memoria principale, ora supponiamo che p1 abbia iniziato la sua esecuzione in modo che venga chiamato il suo primo metodo main e i suoi record di attivazione saranno inseriti nello stack ora chiamate main metodo fun () ed è iniziato eseguendo e dopo qualche tempo questo processo viene anticipato e avviene il cambio di contesto (il puntatore dello stack di p1 salverà l'indirizzo di p1.fun (3)). quindi le chiamate avranno un aspetto simile a questo: -

p1.main () - > p1.fun (5) - > p1.fun (4) - > p1.fun (3)

quindi main è nella parte inferiore dello stack e fun (3) è in cima allo stack, ora p2 ha la possibilità di eseguire l'operazione, quindi il metodo principale del processo p2 verrà eseguito e verrà inserito nella parte superiore del stack cioè sulla parte superiore del record di attivazione di fun (3). così ora sarà simile a questo

p1.main () - > p1.fun (5) - > p1.fun (4) - > p1.fun (3) - > p2.main ()

il suo tempo per p2 per essere preventivato, P1 ottiene di nuovo CPU e avvia l'esecuzione ora p1 riprende il suo contesto e va all'indirizzo del puntatore dello stack e inizia l'esecuzione del codice rimanente.

quindi dopo l'esecuzione di p1, lo stack sarà simile a questo: -

p1.main () - > p1.fun (5) - > p1.fun (4) - > p1.fun (3) - > p2.main () - > p1.fun (2) - > fun (1) - > divertimento (0)

ora p1 avvierà i record di popping: -

p1.fun (0) viene estratto. p1.main () - > p1.fun (5) - > p1.fun (4) - > p1.fun (3) - > p2.main () - > p1.fun (2) - > fun (1)

p1.fun (1) viene estratto. p1.main () - > p1.fun (5) - > p1.fun (4) - > p1.fun (3) - > p2.main () - > p1.fun (2)

p1.fun (2) viene estratto. p1.main () - > p1.fun (5) - > p1.fun (4) - > p1.fun (3) - > p2.main ()

ora p1 è nella CPU e non sarà in grado di eseguirlo perché lo stack top è del processo 2.

So che la mia comprensione è sbagliata, per favore correggimi e dona qualche rifrazione.

    
posta navs4me 25.07.2016 - 18:44
fonte

1 risposta

5

Ciò che manca è che P1 e P2 ottengano ciascuno il proprio stack. Quindi le immagini risultanti sono molto più pulite di quanto immagini.

Hai due stack, uno per P1 e uno per P2. Sono completamente separati. Tutti i frame di attivazione per P1 vanno nello stack P1 e tutti i frame di attivazione per P2 vanno sullo stack P2. Funzionano in modo completamente indipendente e su un processore multi-CPU, possono persino funzionare contemporaneamente.

Un singolo processore (sia su un processore multi-CPU o meno) può alternare tra P1 e P2. Quando un processore interrompe l'esecuzione di P1 e inizia a eseguire P2, questa è una forma dell'interruttore di contesto . Lo stato di P1 viene salvato e viene caricato lo stato P2. Questa operazione di salvataggio / ripristino cambierà in modo efficace gli stack aggiornando i registri della CPU che fanno riferimento allo stack.

Altri termini che potresti voler distinguere:

Un processo di solito riflette un spazio indirizzo . Uno spazio di indirizzamento è come la memoria di un array. Diventa più complicato di così, a causa della memoria condivisa tra i processi (l'altro) e il kernel, e vari meccanismi di protezione; tuttavia, concettualmente, puoi pensare a uno spazio di indirizzamento come una grande serie di byte a partire dall'indice 0. A ogni processo viene assegnato il proprio spazio di indirizzamento, che consente a ciascun processo di avere il proprio stack e heap indipendentemente dagli altri processi, senza preoccuparsi di indici in conflitto (cioè di indirizzi in conflitto).

Ora, un singolo processo può anche eseguire più discussioni . Il tuo esercizio mentale che coinvolge P1 e P2 potrebbe anche essere replicato usando T1 e T2, due thread in un unico processo. Con più thread in un processo, i thread condividono tutti lo stesso spazio di indirizzamento, sebbene T1 e T2 abbiano ancora ciascuno il proprio stack . T1, il primo thread del processo, partirà da main , ma T2 verrà avviato ovunque sia diretto (tramite T1 che avvia un nuovo thread, T2, quindi è improbabile che T2 inizi a main ). Altrimenti gli scenari sono molto simili tra P1, P2 e T1, T2.

Si noti che un cambio di contesto tra P1 e P2 comporta anche la modifica dello spazio degli indirizzi (più registri di cpu che qui si riferiscono allo spazio degli indirizzi), mentre tra T1 e T2 solo lo stato dello stack deve essere cambiato e non lo spazio degli indirizzi. T1 e T2 condivideranno anche un heap comune (sebbene ognuno possa essere indirizzato verso diverse aree di quell'heap per le loro allocazioni) mentre P1 e P2 avranno heap indipendenti (sebbene possano ancora stabilire una certa memoria condivisa).

Gli spazi degli indirizzi sono supportati dalla funzionalità hardware della cpu chiamata memoria virtuale . Ciò consente di separare gli spazi degli indirizzi e protegge un processo da comportamenti potenzialmente erratici di un altro, in quanto a quel processo è impedito di toccare la memoria di un altro (modulo debugger, ecc.).

Potresti anche trovare interessante: link

Fondamentalmente, la cpu ha registri che si riferiscono al contesto del suo attuale stato di esecuzione. Questi registri in qualche modo definiscono, tra l'altro:

  • lo spazio degli indirizzi (da cui sono allocati lo stack (s) e l'heap)
  • lo stack (e quindi i frame di attivazione esistenti, e dove ne vanno di nuovi)
  • il puntatore dell'istruzione, che identifica il flusso di istruzioni corrente

Un interruttore di contesto salva e ripristina queste cose. Salvando il contesto corrente, quel contesto di thread (di un processo) viene sospeso per una successiva ripresa. Ripristinando un altro contesto, viene ripreso quell'altro thread (di un processo).

Si noti che non tutto il contesto deve essere salvato ogni volta, ad esempio, lo spazio degli indirizzi è probabilmente già salvato per il processo, quindi è necessario ricaricarlo solo in fase di ripresa anziché di salvataggio e ricaricamento. Tuttavia, lo stack e il puntatore dell'istruzione si spostano durante l'esecuzione, ovviamente, quindi è necessario salvarli in modo che possano essere ripristinati in seguito.

Quando sono presenti più CPU in esecuzione simultanea, ognuna ha la propria nozione completa di contesto, riferendosi allo spazio degli indirizzi, allo stack di attivazione e al flusso di istruzioni usando i propri registri.

Spetta quindi al sistema operativo e alla gestione dei tempi di esecuzione allocare correttamente il nuovo stack all'interno di un processo quando viene richiesto un nuovo thread o allocare un nuovo spazio indirizzo quando viene richiesto un nuovo processo.

    
risposta data 25.07.2016 - 19:48
fonte

Leggi altre domande sui tag