La gerarchia delle memorie Gestione della memoria Ogni processo
Transcript
La gerarchia delle memorie Gestione della memoria Ogni processo
La gerarchia delle memorie Gestione della memoria Ogni processo, per avanzare necessità che il relativo programma risieda nella memoria centrale, Tale programma è costituito da memoria e dati. Lo spazio fisico è l'insieme degli indirizzi accessibili fisicamente e costituisce la memoria fisica; lo spazio logico è uno spazio astratto di indirizzi logici (l'indirizzo logico della prima istruzione sarà 0), ma durante l'esecuzione gli indirizzi devono essere fisici (l'indirizzo fisico della prima istruzione sarà l'indirizzo della locazione di memoria a partire dalla quale è stato caricato il programma). Occorre pertanto, al fine di eseguire il programma trasformare gli indirizzi logici in indirizzi fisici. Questa traduzione è detta Mapping o Rilocazione degli indirizzi e viene svolta dalla MMU. Registri e Cache I registri rappresentano una piccola parte di memoria utilizzata per velocizzare l'esecuzione dei programmi fornendo un accesso rapido ai valori usati più frequentemente — tipicamente, i valori correntemente in uso in una determinata parte di un calcolo. La Cache è una memoria particolarmente veloce ed ha il compito di tenere le parole di memoria più usate, si incrementa così notevolmente la velocità di risposta della macchina. Viene utilizzata quindi una memoria statica di dimensioni molto limitate (perché estremamente costosa) ma con tempi di accesso molto ridotti (fino all’ordine di un solo ciclo di clock) per contenere le parole di memoria usate più di frequente e quando la CPU richiede una certa parola prima di andarla a ricercare in memoria centrale si verifica se essa è presente nella cache. Si distinguono i diversi livelli di cache per aumentare ancora di più la scalabilità: L1: cache interna al processore, solitamente sincrona con il processore, spesso distinta per dati e istruzioni (split cache) L2: generalmente unificata per dati e istruzioni, può essere interna o esterna L3: generalmente unificata per dati e istruzioni, può essere o meno presente ed è interna o esterna Il principio tramite il quale si sceglie cosa mettere in cache è noto col nome di “principio di località” che asserisce che se viene indirizzata una parola, supponiamo all’indirizzo di memoria A, è molto probabile che le successive e precedenti richieste (in ordine cronologico) siano riferite a locazioni di memoria vicine ad A Per la gestione della memoria esistono diverse tecniche: La rilocazione Nel momento in cui il processo deve essere eseguito allora è necessario caricarlo in memoria fisica. Tutti gli indirizzi virtuali del processo devono essere rilocati in memoria fisica. Rilocare un processo significa tradurre tutti gli indirizzi virtuali di quel processo nei corrispondenti indirizzi fisici tramite una funzione di rilocazione. Esistono due tecniche per fare ciò: rilocazione statica e rilocazione dinamica. Nel caso di rilocazione statica è il loader, che prende anche il nome di caricatore rilocante, che prima di caricare il programma in memoria traduce tutti gli indirizzi virtuali in indirizzi fisici. Nel caso di rilocazione dinamica è invece un dispositivo hardware, la MMU, residente nello stesso chip del processore, che effettua la traduzione degli indirizzi logici in indirizzi fisici durante l’esecuzione del processo ovvero run time. La memoria virtuale di un processo parte dall’indirizzo 0 mentre in memoria fisica in generale il caricamento avviene a partire da un indirizzo I diverso da zero. Partizioni fisse La memoria è divisa, al momento dell'installazione del SO in partizioni di dimensione fissa, non necessariamente uguali, ciascuna delle quali potrà contenere un intero programma. Il SO, per la gestione farà uso della tabella delle partizioni di tante righe quante sono le partizioni in memoria, ed ognuna conterrà per ogni partizione, indirizzo iniziale, indirizzo finale, PID del programma che la occupa (0 se la partizione è libera). La rilocazione è statica gli indirizzi vengono quindi trasformati da logici a fisici al momento del caricamento in memoria del programma. Il grado di multiprogrammazione è fisso in quanto si può caricare un solo programma per partizione. Facendo uso della partizione fissa si avrà il problema della frammentazione interna in quanto la locazione deve essere più grande del programma caricato su essa. Contigua singola La memoria è divisa in due parti, una riservata al sistema operativo ed un'altra ai programmi utente. Il programma sarà sempre caricato in memoria centrale a partire dallo stesso indirizzo fisico. Dal momento che è possibile caricare un solo programma alla volta in memoria, è impossibile fare uso della multiprogrammazione. La rilocazione è assoluta, in quanto come si evince anche da quanto detto sopra, gli indirizzi nei quali verrà immagazzinato il programma sono sempre gli stessi, pertanto gli indirizzi vengono trasformati da logici a fisici al momento della creazione del programma eseguibile. Partizioni variabili Le partizioni vengono create dinamicamente in base alle richieste. Inizialmente si hanno due partizioni una per il SO e una, che rappresenta tutto il resto della memoria libera, dedicata ai programmi utente. Quando è necessario allocare programmi in memoria, essi vengono inseriti in maniera sequenziale. Quando un programma termina la sua esecuzione libera la memoria e la sua partizione, se vi sono partizioni libere adiacente si procede con la compattazione in un’unica partizione più grande. Cosi procedendo però si andrà in contro a frammentazione esterna poiché lo spazio non allocato non fa parte di una partizione usata da un programma, come invece avviene con la frammentazione interna. Il SO per la gestione della memoria farà uso di una tabella delle partizioni, che contiene l’indirizzo di base nella memoria fisica, simile a quella della partizione fissa con la differenza che il numero di righe stavolta sarà variabile, poiché abbiamo un numero variabile di partizioni. La scelta della partizione libera da utilizzare può essere fatta con diverse strategie: First fit: viene scelta la prima partizione sufficiente a contenere il programma; favorisce il formarsi di una grande partizione libera (verso il fondo della memoria) Best fit: viene scelta la più piccola partizione sufficiente a contenere il programma, così da non frammentare inutilmente le grosse partizioni Worst fit: viene scelta la più grande partizione che può contenere il programma La rilocazione può anche essere statica, ma in genere è dinamica: il programma viene caricato in memoria senza alcuna trasformazione di indirizzi, che vengono trasformati al momento dell'esecuzione. L'indirizzo iniziale della partizione viene posto in uno speciale registro base e durante l'esecuzione l'indirizzo fisico viene calcolato sommando all'indirizzo logico il contenuto del registro base. In presenza di rilocazione dinamica, per eliminare la frammentazione, è possibile effettuare il compattamento delle partizioni occupate in modo da creare un’unica grande partizione libera, risultato della somma di tutti i frammenti liberi, operazione questa però abbastanza costosa in termini di overhead. Segmentazione Le tecniche di gestione della memoria fin qui illustrate prevedono che ogni programma sia logicamente lineare e venga caricato in un'area contigua di memoria. Con la segmentazione questo vincolo cade, in quanto ogni programma viene suddiviso in più segmenti, ciascuno dei quali potrà essere successivamente caricato in una diversa partizione. In ogni segmento gli indirizzo logici partono da 0; il campo indirizzo delle istruzione è formato numero del segmento (indirizzo segmento 1 byte) e displacment (indirizzo all'interno del segmento 2 byte). La memoria centrale viene gestita a partizioni variabili, con l'ausilio della tabella delle partizioni, contenente, per ogni partizione, indirizzo iniziale, indirizzo finale, PID e numero del segmento del programma che la occupa (0 se libera). La rilocazione è dinamica e per risolvere gli indirizzi per ogni programma si usa una tabella dei segmenti (di tante righe quanti sono i segmenti del programma e avente, per ogni segmento, l'indirizzo della partizione nella quale è stato caricato il relativo segmento del programma) il cui indirizzo è contenuto nel registro base. Paginazione La segmentazione ridimensiona il problema della frammentazione, ma non lo elimina. La tecnica della paginazione è simile a quella della segmentazione, con la differenza che le pagine sono di lunghezza fissa(1kb, 2kb, 4kb). Il programma viene, suddiviso in pagine a lunghezza fissa dal compilatore. La memoria centrale viene suddivisa in pagine di uguale dimensione adatte a contenere porzioni di programma di eguale misura. Le pagine del programma possono essere caricate ovunque nella memoria pertanto la memoria viene sfruttata al 100%(in po' di spreco si ha soltanto nell'ultima pagina). Il campo indirizzi delle istruzioni, come per la segmentazione è formato da numero pagina e displacement (indirizzo all'interno della pagina). Il sistema operativo gestisce la memoria per mezzo della tabella di occupazione pagine, di tante righe quante sono le pagine, contenente, per ogni riga l'indicazione di chi occupa la pagina corrispondente (PID e numero pagina del processo che la occupa o 0 se è libera). La rilocazione è dinamica e per risolvere gli indirizzi, per ogni programma si usa una tabella delle pagine, il cui indirizzo è contenuto nel registro base. MMU e TLB L'unità di gestione della memoria, o memory management unit, in sigla MMU, è una classe di componenti hardware che gestisce le richieste di accesso alla memoria generate dalla CPU. La MMU può avere vari compiti tra cui la traslazione (o traduzione) degli indirizzi virtuali in indirizzi fisici (necessaria per la gestione della memoria), la protezione della memoria, il controllo della cache della CPU. La MMU contiene una tabella delle pagine indicizzata dal numero della pagina. Ogni elemento di questa tabella (detto PTE o Page Table Entry) restituisce il numero fisico della pagina corrispondente a quello virtuale, che, combinato con l'offset della pagina, forma l'indirizzo fisico completo. Il Translation Lookaside Buffer (TLB) è un buffer, cioè una memoria tampone che l'MMU (Memory Management Unit) usa per velocizzare la traduzione degli Indirizzi Virtuali. Il TLB possiede un numero fisso di elementi della Page Table, la quale viene usata per mappare gli Indirizzi Virtuali in Indirizzi Fisici. Segmentazione paginata Questa tecnica prevede sia la segmentazione che la paginazione: i programma sono suddivisi in segmenti che, a loro volta, sono suddivisi in pagine e la memoria centrale è suddivisa in pagine. Il campo indirizzo delle istruzioni è composto da numero segmento, numero pagina e displacement. Per ogni programma c'è una tabella dei segmenti, puntata dal registro base, ogni riga della quale punta alla tabella delle pagine del relativo segmento. Il page fault Quando una pagina richiesta non è ancora stata caricata in memoria allora si verifica un page fault. Il page fault viene gestito dal sistema operativo che provvede a caricare da disco in memoria la pagina richiesta. Il processo che ha provocato il page fault viene sospeso e il suo contesto salvato nel suo descrittore. Il sistema operativo prima di caricare la pagina richiesta controlla se in memoria fisica c'è un frame libero. Se non c'è alcun frame libero allora viene cercata una pagina da rimpiazzare. Il rimpiazzamento avviene usando un opportuno algoritmo (Ad esempio FIFO) Caricamento dinamico e Memoria virtuale Con le precedenti tecniche di gestione della memoria i processi in esecuzione devono essere caricati completamente in memoria. Il caricamento dinamico, invece, consente che un processo sia parzialmente presente in memoria centrale e che le sue parti siano caricate e scaricate dalla memoria secondaria quando serve. Ad ogni processo è assegnata un'area di memoria centrale (che lo contiene parzialmente) ed un'area su disco, area di swap, che contiene la sua immagine, istruzione e dati, e viene aggiornata durante l'esecuzione. I processi, oltre alla memoria centrale, usano anche l'area di swap su disco, usano cioè una memoria virtuale più grande della memoria fisica centrale. Il loro spazio indirizzabile, indirizzi logici, può essere maggiore dello spazio fisico della memoria centrale e possono essere eseguiti contemporaneamente tanti processi dei quali è presente in memoria solo una piccola parte. A fronte di questo indubbio vantaggio, occorre pagare un prezzo: lo swapping – carico e scarico (swap in swap out) di processi da/su area di swap – rallenta l'esecuzione del processo (il tempo di accesso alla ram è dell'ordine dei nanosecondi mentre quello del disco è dell'ordine dei millisecondi). Paginazione dinamica Questa tecnica permette di eseguire un programma anche se non tutte le sue pagine sono presenti in memoria; le altre pagine restano nell'area di swap e vengono caricate all'occorrenza. Quando il programma fa riferimento ad una pagina non presente in memoria (page fault) il sistema provvede a caricarla (page swapping) e il processo viene bloccato finché la pagina non è stata caricata. La nuova pagina sarà caricata su una pagina libera, se è presente, o su una occupata; in tal caso potrebbe prima essere necessario salvare la pagina da sovrascrivere su disco. La scelta della pagina da sovrascrivere, quando tutte le pagine sono occupate, si procedete con la sostituzione utilizzando ad esempio, il seguente algoritmo. Algoritmo LRU L'algoritmo di sostituzione utilizzato di solito è LRU(Least recently used) che si basa sul principio di località globale se una pagina è stata usata di recente vuol dire che probabilmente verrà riutilizzata a breve. Quindi, quando occorre sovrascrivere una pagina, si sceglie una pagina non usata di recente. Paginazione su richiesta Le pagine virtuali di un processo non sono caricate tutte insieme ma vengono caricate solo quando richieste. Se la pagina virtuale richiesta non è presente in memoria viene generato un page fault che viene gestito dal sistema operativo come spiegato nei paragrafi precedenti.