Sei sulla pagina 1di 6

Object 4

1
2
3

Gestione della Memoria nei Sistemi Operativi


Le memorie (RAM) in uso sui calcolatori odierni, pur essendo infinitamente superiori a quelle in uso solo qualche
anno fa, non sono infinite. Allo stesso tempo i moderni calcolatori si trovano a gestire un numero di programmi
nettamente superiore a quelli presenti in precedenza e con dimensioni sempre maggiori. Le memorie non sono
tutte uguali, in primo luogo esse differiscono per dimensione, avere una memoria da un megabyte non è certo
come avere una memoria da un gigabyte, o da un terabyte:
Ricordiamo:

• Kilobyte (KB): 1024 byte,


• Megabyte (MB): 1024 kilobyte,
• Terabyte (TB): 1024 megabyte,
• Petabyte (PT): 1024 terabyte,
I computer attuali hanno in genere dischi rigidi con una capacità compresa tra i 500 GB e i 2 TB. Mentre le
memorie RAM vanno in genere dai 4 agli 8 GB (o almeno, queste sono le capacità standard nel momento in cui
scrivo).
I dischi si differenziano anche per volatilità, ovvero la capacità di conservare (o no) le informazioni in assenza di
una fonte elettrica. I dischi rigidi o hard disk sono memorie non volatili, vale a dire che mantengono le
informazioni anche a computer spento, in assenza di alimentazione, le RAM (Random Access Memory) sono
invece delle memorie volatili. Infine, i dischi differiscono anche per costo, che in genere è un valore connesso alla
dimensione e alla velocità dell’unità. Parleremo più nel dettaglio dei dispositivi di output/input in un capitolo a
loro dedicato.

Prestazioni
Per migliorare le prestazioni generali del sistema, i processi devono essere spostati dal disco alla memoria (RAM).
L’accesso al disco è, infatti, molto costoso (dal punto di vista dei cicli di CPU), l’accesso alla RAM è invece più
veloce. Anche con queste premesse l’accesso alla RAM rimane una operazione piuttosto costosa che deve essere
ridotta al minimo. I tempi e la frequenza di accesso alla memoria hanno quindi un peso importante nelle
prestazioni di un sistema.
Confronto sui tempi di accesso:

• Tempi di accesso al buffer cache presente in RAM:


• Tempi di accesso al disco:

Modelli senza astrazione


I primi computer mainframe antecedenti il 1960 e i personal computer prima del 1980 non possedevano un
modello di astrazione. I processi in esecuzione su questi sistemi avevano pieno accesso alla memoria, inoltre essi
eseguivano questo accesso in modo diretto, senza l’uso di indirizzi logici.
Questo causava alcuni problemi:
• Era difficile fare girare più programmi sullo stesso computer, essi potevano usare aree di memoria
comune;
• I programmi potevano modificare o cancellare il sistema operativo.

Tecniche per la multi-programmazione su un sistema senza astrazione


Anche con queste premesse in taluni casi era comunque possibile che un sistema operativo permettesse
l’esecuzione simultanea di due o più programmi. Il primo problema da risolvere riguardava la condivisione della
memoria contenente i vari processi. Senza volerlo, infatti, i processi potevano richiamare o sovrascrivere aree di
memoria destinate ad altri, provocandone il crash.
Per evitare che tali problemi si verificassero sono state sviluppate diverse tecniche:
• Swapping, tecnica che permette di spostare (abbastanza) velocemente i dati di un processo dal disco alla
memoria e viceversa. Vedremo questa tecnica nel dettaglio più avanti;
• Uso delle Program Status Word, utilizzo di chiavi create dal sistema e consegnate ai singoli processi che
permettevano l’accesso sicuro alle proprie aree di memoria;
• Riallocazione statica.

L’avvento della MMU


Sui moderni computer è stato introdotto un dispositivo noto come Memory Management Unit (MMU), con il
compito di occuparsi della gestione della memoria. La MMU risolve i due problemi principali legati alla gestione
della memoria: la protezione e il riposizionamento. L’esecuzione di più processi simultanei è oggi possibile solo
se la Memory Management Unit è in grado di garantirne la coesistenza.

Riallocazione dinamica della MMU

Spazio degli indirizzi e differenza tra indirizzi logici e fisici


Come abbiamo già visto in precedenza la memoria principale viene suddivisa in diversi spazi degli indirizzi, che
rappresentano le porzioni di memoria accessibili ai singoli programmi. Per funzionare questa tecnica fa uso della
riallocazione dinamica, questa tecnica permette di mappare lo spazio degli indirizzi di ogni processo su di una
parte diversa della memoria fisica. Successivamente vengono utilizzati i registri base e limite, presenti nel
processore, per tenere traccia dell’inizio e della fine dello spazio degli indirizzi del processo in esecuzione.
Gli indirizzi si dividono in:
• Indirizzi fisici, a ogni indirizzo fisico corrisponde una locazione di memoria nella RAM;
• Indirizzo logico, a ogni indirizzo logico (o virtuale) corrisponde un dato o una istruzione di un processo.
I simboli usati nel programma (variabili), vengono convertiti in indirizzi logici dal compilatore ed infine in
indirizzi fisici dalla Memory Management Unit. Essa ha infatti il compito di mappare gli indirizzi logici su quelli
fisici evitando le sovrapposizioni. La CPU elabora gli indirizzi logici e lascia alla MMU il compito di mapparli
sulla memoria. Questo processo di astrazione svincola il programmatore dalla struttura della memoria, esso non si
dovrà preoccupare di definire gli indirizzi fisici che andrà a usare il proprio programma, si limiterà solo a usare
degli indirizzi logici.

Binding
L’operazione di traduzione delle variabili in indirizzi viene detta binding. Il binding può essere svolto in modo
statico (early binding), in delayed binding, oppure in maniera dinamica (late binding). L’early binding viene svolto
dal compilatore, questo permette al compilatore di anticipare alcune operazioni e di produrre un codice efficiente.
Tuttavia questo processo può essere svolto solo se in fase di compilazione è già nota la memoria fisica che userà il
processo, qualora questo spazio dovesse cambiare occorrerà ricompilare il codice dell’intero programma. Il
delayed binding è concettualmente simile al precedente ma sposta la traduzione nel linker. Questo sistema produce
codice efficiente e permette compilazioni separate. Infine, il late binding prevede l’esecuzione di questa
conversione in runtime, il codice prodotto sarà quindi poco efficiente ma molto flessibile. Sono un esempio di late
binding l’overlay, la memoria virtuale e i link/loader dinamici. Questo sistema non necessita ovviamente della
conoscenza da parte del compilatore/linker dello spazio che sarà utilizzato dal processo.

Swapping e Memoria Virtuale


Come abbiamo visto esistono alcune tecniche che ci permettono di eseguire più processi in parallelo sulla stessa
macchina. Queste tecniche ci permettono di far convivere due processi evitando che questi si danneggino a
vicenda, tuttavia difficilmente ci aiuteranno nel caso in cui volessimo eseguire processi la cui dimensione totale
supera la dimensione della RAM. L’utilizzo di queste tecniche è comunque essenziale sui computer moderni.

Swapping
Lo swapping è una tecnica che permette di spostare i processi inattivi dalla memoria (RAM) al disco. In questo
modo si riesce a recuperare dello spazio di memoria quando un processo si mette in attesa, o comunque quando
non è in esecuzione. Come abbiamo visto questa tecnica può essere usata anche per evitare i conflitti su sistemi
senza MMU. L’utilizzo di questa tecnica crea numerosi vuoti all’interno della memoria, questi possono essere
compattati tramite un’operazione di Memory Compaction. Le operazioni di Memory Compaction sono utili,
tuttavia esse si dimostrano molto lente e pesanti. In alternativa è possibile lasciare libere queste aree e in modo
tale da farle usare dagli altri processi.

Memoria Virtuale
Un’altra tecnica molto utile ed efficace prevede l’utilizzo della memoria virtuale, questa tecnica è molto più
complessa della precedente per questo sarà trattata in un capitolo a parte.

Gestione della Memoria


La memoria a disposizione del calcolatore viene assegnata dinamicamente ai processi dalla Memory Management
Unit, in seguito tale memoria deve necessariamente essere gestita. Attualmente esistono diverse strategie utili alla
gestione della memoria, queste strategie vengono valutate e classificate in funzione di tre criteri distinti:
1. Quanti processi diversi sono in grado di gestire;
2. Quanto spazio lasciano inutilizzato;
3. Quanto sono facili da implementare.
Partizioni multiple fisse
La memoria viene suddivisa in blocchi di dimensioni fissa e diversa, a cui vengono assegnati i vari processi.
Questo sistema si suddivide a sua volta in due modelli implementativi:
• Code multiple, i processi vengono suddivisi in code a seconda della loro dimensione. Ogni coda farà
riferimento a una determinata partizione con una particolare dimensione;
• Code singole, i processi sono mantenuti all’interno di un’unica coda e poi smistati nelle varie partizioni.

Per la scelta del modello più conveniente si fa riferimento al modello matematico della teoria delle code.
Vantaggi:
• Le partizioni fisse hanno particolare successo sui sistemi batch, grazie alla staticità dei programmi
eseguiti.
Svantaggi:
• Entrambi i sistemi provocano una gestione difficoltosa in caso di molti programmi di dimensione
variabile;
• Entrambi i sistemi provocano una gestione difficoltosa dei programmi la cui dimensione totale è maggiore
della dimensione della RAM.

Partizioni multiple variabili


Le dimensioni dei processi non si mantengono costanti durante tutta la loro esecuzione, per questo motivo bisogna
prevedere la possibilità di aumentare la memoria messa a disposizione di un processo.

Il partizionamento variabile utilizza lo swapping per gestire lo spostamento dei processi dentro e fuori dalla
memoria. Questo provoca la formazione di numerose aree libere tra i vari processi. La tecnica più efficiente per
gestire questi spazi consiste nel lasciarli dove sono, e utilizzare in un secondo momento questi spazi per fare
espandere i processi.
Gestione degli spazi di memoria
A prescindere dalla tecnica di allocazione in uso sul sistema corrente sarà opportuno predisporre un sistema per
gestire gli spazi liberi/occupati presenti nella memoria. In particolare occorre creare e mantenere una struttura dati
in grado di conservare l’informazione circa la disponibilità delle celle di memoria e di conseguenza che permetta
di capire quali spazi della memoria sono stati allocati e quali sono invece disponibili.
In questo modo sarà possibile evitare la sovrascrizione dei nuovi dati sui precedenti, oltre che facilitare la scelta
della locazione di memoria in cui iniziare a scrivere la nuova informazione (in relazione alla dimensione del dato
da inserire e dello spazio libero in cui inserirlo).

Gestione della Memoria con Bitmap


La memoria viene suddivisa in unità di allocazione unitaria (questa allocazione varia però nei diversi sistemi). A
ogni unità di allocazione corrisponde un bit sulla bitmap, tale bit avrà valore ‘0’ se lo spazio corrispondente è
vuoto, oppure ‘1’ se lo spazio corrispondente è pieno. Quando si vuole portare un programma di ‘k’ unità di
memoria in RAM la Memory Management Unit, si dovrà cercare nella bitmap uno spazio di ‘k’ unità libere.
Questo processo è molto lento (a causa degli elevati tempi di ricerca e di accesso), specialmente su bitmap molto
grandi.

Gestione della Memoria con Liste


Un secondo sistema prevede l’utilizzo delle liste per tenere traccia degli spazi vuoti/pieni all’interno della
memoria. Ogni voce della lista specifica uno spazio vuoto (H) o pieno (P), in aggiunta all’indirizzo di partenza del
blocco e della sua lunghezza. Le liste possono essere collegate tra loro tramite un puntatore “successivo” o due
puntatori “successivo” e  “precedente”.
Strategie di allocazione dei bit liberi
Esistono diverse strategie per facilitare e migliorare la ricerca di una porzione di memoria libera:
• First fit: viene scelta la prima posizione libera sufficientemente grande per contenere il processo;
• Next fit: simile al precedente ma a ogni ciclo riprende la ricerca da dove si era fermata la volta
precedente;
• Best fit: viene scelta la più piccola porzione di memoria che può contenere il processo;
• Worst fit: viene scelta la più grande porzione di memoria che può contenere il processo.