# Gestione della memoria principale

La memoria principale, spesso chiamata semplicemente "memoria", è l'insieme dei dispositivi di memorizzazione esterni al processore ma indirizzabili esplicitamente da istruzioni in linguaggio macchina. I problemi che deve risolvere il sistema operativo relativamente alla memoria sono i seguenti:
* Suddividere equamente la memoria principale tra i processi in esecuzione.
* Impedire che ogni processo acceda (cioè legga o scriva) alle parti di memoria principale assegnate ad altri processi o al sistema operativo stesso (protezione della memoria).
* Per sfruttare meglio la memoria principale disponibile, spostare trasparentemente su memoria secondaria (normalmente un disco fisso) il contenuto di porzioni inutilizzate di memoria principale, e riportare altrettanto trasparentemente in memoria principale dalla memoria secondaria le informazioni indirizzate dai processi.
Per agevolare la risoluzione di questi problemi, i processori sono stati dotati di una componente hardware chiamata "unità di gestione della memoria" (o MMU, dall'inglese Memory Management Unit). L'uso della MMU consente di avere due concetti di memoria: la "memoria reale" e la "memoria virtuale".

## Protezione della memoria

Vediamo però prima i tentativi di gestire la memoria in sistemi multiprogrammati in assenza di MMU, e quindi con la sola memoria reale. In realtà per risolvere il problema della protezione della memoria è comunque necessario un supporto hardware, costituito dal registro base (registro che contiene il primo indirizzo fisico legale, cioè utilizzabile) e dal registro limite (registro che contiene la soglia di indirizzamento, ovvero la dimensione del range di memoria accessibile). Quando viene creato un processo, si cerca nella memoria un intervallo di indirizzi liberi abbastanza grande da contenere il codice del programma e lo spazio per i dati. Ogni volta che il processo esegue un'istruzione di accesso alla memoria, un circuito somma l'indirizzo al contenuto del registro base, e questo fornisce l'indirizzo reale della cella indirizzata. Contemporaneamente, un altro circuito verifica che l'indirizzo emesso dal processore abbia un valore inferiore alla soglia dello spazio di indirizzamento del processo. In caso negativo, viene sollevata un'eccezione hardware e il controllo torna al sistema operativo.
Questi due registri non vengono usati nell'eseguire il codice del sistema operativo stesso, e vengono reimpostati ogni volta che il sistema operativo passa il controllo a un altro processo.
Tale architettura assume che ogni processo abbia codice e dati di lunghezza definita in fase di compilazione. Infatti, i primi linguaggi di programmazione ad alto livello, FORTRAN e COBOL, ammettevano solamente dati statici.

## Frammentazione della memoria

Un altro problema con questa architettura era quello della gestione della memoria dinamica. Infatti, se si creano due processi e poi il primo termina, rimane uno spazio libero prima dello spazio occupato dal secondo processo. Quando si crea un terzo processo, tale spazio potrebbe essere troppo piccolo, per cui è necessario cercare uno spazio sufficientemente grande. Ci sono vari algoritmi di allocazione di segmenti di memoria, ma tutti soffrono di problemi di prestazioni e di frammentazione della memoria. Si ha la memoria frammentata quando si vorrebbe allocare un nuovo segmento (per lanciare un nuovo processo), ma non c'è nessun intervallo libero abbastanza grande, e tuttavia la somma degli intervalli liberi sarebbe abbastanza grande. Alcuni algoritmi di allocazione della memoria, eseguono saltuariamente un compattamento della memoria per deframmentarla. Il compattamento consiste nel traslare il contenuto di segmenti di memoria in modo da concentrare alla fine tutto lo spazio libero.