venerdì 5 marzo 2010

Gestione della memoria


 Memoria Centrale.
Visto che l'obiettivo di avere un sistema efficiente ha portato alla multiprogrammazione, ossia l'alternarsi di piu' processi in cpu, la memoria centrale dovra' ospitare diversi processi, e siccome non e' grande abbastanza da contenerli tutti, dovra' essere gestita dal sistema operativo nel modo piu' efficiente possibile.

Il ciclo di cpu prevede 3 istruzioni principali: fetch decode execute, prevede la lettura e scrittura oltre all'esecuzione vera e propria, da e su dispositivi di memorizzazione.
Questi possono essere accessibili direttamente dal processore.
Alla memoria centrale si accede tramite bus.
La questione di velocita' d'accesso e' coperta dalla memoria cache, un dispositivo di memorizzazione che sfrutta principi di localita' e temportali per cui si controlla se un'istruzione e' presente in cache o bisogna andare a prelevarla in memoria, ma un fattore altrettanto importante oltre alla cache e' la velocita' di protezione.
La memoria e' protetta grazie al meccanismo di protezione hardware offerto da due registri base e limite.
Quando un processo richiede accesso ad un'area di memoria si controllano gli indirizzi attraverso questi due registri: il registro base contiene il piu' piccolo indirizzo a cui puo' accerdere, il registro limite contiene l'inervallo di indirizzi a cui il processo puo' accedere.
Un tentativo di accesso a intervalli di memoria sbagliata comporta una trap.
Quando un programma utente viene scelto per l'esecuzione puo' venire caricato in memoria passando per vari stadi in cui gli indirizzi sono rappresentati in modo diverso.
Infatti gli indirizzi del codice sorgente sono simbolici, un compilatore li rende rilocabili ed un loader li fa corrispondere ad indirizzi assoluti.
I metodi di associazione degli indirizzi nella fasi di compilazione e caricamento producono indirizzi logici e fisici identici, mentre nella fase d'esecuzione l'associazione degli indirizzi e' compito dell'MMU.

L'MMU e' un dispositivo che contiene un registro di rilocazione che ha un valore a cui deve essere sommato l'indirizzo logico per ottenere l'indirizzo fisico.
La separazioned degi indirizzi logici e fisici e' un primo passo verso tecniche di memorizzazione molto efficienti.
In primo luogo e' possibile avere un caricamento dinamico di procedure, che occupano la memoria solo quando vengono chiamate, o di librerie, che ancor meglio vengono linkate, in modo che durante l'esecuzione di un programma il riferimento fa si che venga ricercata la libreria linkata e se non e' presente viene caricata in memoria. Questo meccanismo e' molto utile per l'aggiornamento delle librerie.

Il caricamento in memoria delle immagini dei processi e' detto swapping, o avvicendamento, e' l'atto di caricare dalla memoria ausiliaria in memoria centrale i processi che richiede la cpu.
La coda dei processi pronti conserva puntatori alle immagini dei processi (in memoria secondaria).

La gestione della memoria segue un percorso siffatto: inizialmente la cosa piu' importante e' la rilocazione, ossia voler caricare processi in memoria, e scaricarli, ma dividendo lo spazio degli indirizzi logici da quello degli indirizzi fisici, in modo tale che siamo capaci uno, di swappare i processi per rendere efficiente l'utilizzo del processore, e due di rilocare i processi caricati di nuovo in memoria in altre porzioni.
Dopo il primo aspetto della rilocazione vi e' quello della protezione, quindi la stessa separazione degli indirizzi pone dei limiti di permessi che i processi sono costretti a seguire attraverso registri di rilocazione.
Si parla di associazione dinamica degli indirizzi in runtime (MMU).

Ma la principale funzione della gestione della memoria e' quella di organizzarla per accogliere piu' processi.
Un primo approccio e' quello di suddividere la memoria ( spazio contiguo di indirizzi) in partizioni di dimensione fissa.

Il partizionamento fisso e' una tecnica per la quale vengono scelti i processi da una coda d'ingresso per essere inseriti in memoria.
Sono 3 le strategie di posizionamento : best fit, il processo viene messo nel buco di memoria piu' adatto, first fit, il processo occupera' la posizione di memoria che lo contiene che incontra per prima, worst fit: il processo sara' accolto dalla partizione piu' comoda.
Vi e' uno spreco enorme di memoria, che genera due problematiche gravi: la frammentazione interna si ha all'interno della partizione fissa che accogli un processo quindi l'algoritmo di partizionamento worst fit e' quello che causa maggior danno in termini di framentazinoe interna.
La frammentazione esterna e' data dal fatto che non tutte le partizioni vengono occupate.
La soluzione e' in primo luogo il partizionamento dinamico, in cui le partizioni occupate possono essere in un certo modo accorpate per cui creano uno spazio partizionabile ricorsivamente.
Comunque non risolverebbe la frammentazione esterna.
Per evitare questi due problemi bisogna permettere che lo spazio di memoria non sia contiguo.
Questo vuol dire allocare un processo in segmenti di memoria sparsi, non adiacenti.
Per farlo oltre a dividere la memoria si dovra' dividere lo spazio logico, per poi avere dei riferimenti.
La memoria centrale si divide in frames, il processo, o meglio la memoria logica che lo rappresenta, in pagine.
Le pagine e i frame hanno uguale dimensione, che e' data dall'architettura della macchina, ed e' compresa tra 512 byte e 16 Mb. Ed e' una potenza di 2, perche' se lo spazio di indirizzi logici e' grande 2 alla m e una pagina e' 2 alla n, m-n sono il numero delle pagine ed n e' l'offset.
L'architettura di paginazione funziona cosi:
la cpu genera un indirizzo logico, che e' diviso in due, un numero di pagina, rappresentato da m-n bit, il numero di pagina punta alla page table, un riferimento ai frame corrispondenti, presente in ogni processo, o meglio ogni pcb ha un puntatore ad una struttura dati che rappresenta la tabella delle pagine.
Una volta individuato il numero di frame si somma l'offset per ottere l'indirizzo del frame fisico in memoria centrale.

Ma com'e' realizzata una page table?
Si puo' implementare attraverso una serie di registri che la cpu carica ogni volta dopo che il pcb espone il puntatore che indica i valori che deve avere, ma la tabella delle pagine e' molto grande, quindi non sarebbe contenuta in registri. Un metodo alternativo e' utilizzare un solo registro, in cui viene caricata la tabella delle pagine del processo in esecuzione, Page Table Base Register, quindi occorre accedere al PTBR, e poi al frame per ottenere un numero di frame...e due accessi alla memoria non rendono la gestione molto efficiente.
Quindi si utilizza il TLB: Translation Look Aside Buffer, che funziona come una cache, ossia mantiene alcuni numeri di frame per cui la parte dell'indirizzo logico relativa al numero di pagina indica un campo del TLB, e se vi e' presente il numero di pagina si evita l'accesso in memoria, altrimenti si accede alla page table.

Logicamente ci vuole una protezione interna alla paginazione, ma e' facile aggiungere nella struttura dati della page table un campo che indica se il riferimento e' valido o meno, ossia se il bit di una pagina e' impostato a valido vuol dire che quella pagina risiede nello spazio logico del processo.

La paginazione consente di condividere spazi di memoria: se un codice e' rientrante o puro, questo non viene modificato durante l'esecuzione, quindi puo' risiedere in memoria senza copie ed essere utilizzato da piu' preocessi.

Nessun commento:

How to deploy Podman images to OpenShift Container Platform (CRC on localhost)

I have a microservice on localhost and I want to deploy its Podman image on OCP, which I am running using CRC on localhost.       1. Get the...