Contenuti
- Generalità
- Tecniche di Gestione della Memoria
- Allocazione Contigua
- Paginazione
- Segmentazione
- Segmentazione Paginata
Generalità sulla Gestione della Memoria Centrale
La condivisione della memoria da parte di più processi è un aspetto essenziale in
quanto influenza direttamente l'efficienza del sistema (tempo di risposta, utilizzo della
CPU, ... )
Si devono affrontare le seguenti problematiche
- allocazione della memoria ai singoli job/processi
- protezione dello spazio di indirizzamento
- condivisione dello spazio di indirizzamento
- gestione dello swapping
Si tenga presente che nei moderni sistemi la gestione della memoria risulta
inseparabile dal concetto di memoria virtuale (argomento che tratteremo nelle
prossime lezioni)
Vincolo di Funzionamento della Memoria
Vincolo di funzionamento: al fine di essere eseguito, un programma deve essere
portato in memoria e trasformato in un processo. Infatti:
- la CPU preleva le istruzioni da eseguire dalla memoria in base al valore del
program counter
- ogni istruzione viene decodificata e può prevedere il prelievo di operandi dalla
memoria
- al termine dell'esecuzione dell'istruzione, il risultato può dover essere scritto in
memoria
- al terminare del processo, la "sua" memoria viene rilasciata
Ma di che (indirizzi di) memoria stiamo parlando?
Indirizzi di Memoria
- La "trasformazione" di un programma in una entità dinamica, il processo,
avviene attraversando alcune fasi precedenti alla esecuzione vera e propria
(compilazione, collegamento, ... ).
- In ogni fase si assume una diversa semantica degli indirizzi e dello spazio di
memoria
- ciò introduce una distinzione tra
spazio degli indirizzi logici e spazio degli indirizzi fisici
- Gli indirizzi hanno quindi diverse rappresentazioni nelle varie fasi di costruzione
di un programma
Binding degli Indirizzi
- gli indirizzi del programma sorgente sono simbolici
- nella fase di compilazione si associano agli indirizzi simbolici degli indirizzi
rilocabili
- nella fase di collegamento (linker) o di caricamento (loader) si associano agli
indirizzi rilocabili degli indirizzi assoluti
Lib
X
100
1100
21100
memoria)
Prog
Sorgente
Compilatore
Modulo
oggetto
Linker
Modulo
caricabile
Loader
immagine
Tempo di
compilazione
Tempo di
caricamento
Tempo di
esecuzione
L'associazione tra indirizzi simbolici e indirizzi fisici è detta binding
Binding degli Indirizzi: Momenti di Associazione
Il binding di dati/istruzioni a indirizzi assoluti può avvenire in vari momenti:
- Al tempo di compilazione: (binding statico al compile time)
- se la posizione nella memoria centrale ove andrà collocato il programma è nota a
priori, si può generare un codice assoluto
- se si vuole collocare il programma in una posizione diversa, si deve ricompilare
- Al tempo di caricamento: (binding statico al load time)
- per fare ciò si deve generare (tramite la compilazione) del codice rilocabile
(=riposizionabile)
- tutti gli indirizzi sono relativi (all'inizio del programma)
- se si vuole cambiare la collocazione, basta ricaricare
- Al tempo di esecuzione: (binding dinamico, al run time)
- in questo caso il binding è posticipato: il processo può essere spostato, durante
l'esecuzione, in posizioni diverse della memoria
- è richiesto un adeguato supporto hardware (per garantire l'efficienza: ad es. registri
base e limite)
Binding degli Indirizzi: Rappresentazione Schematica
Schematicamente, la CPU genera indirizzi logici che corrispondono a locazioni di
memoria fisica:
Indirizzo
logico
Indirizzo
fisico
CPU
MEM
STATICO:
Compile time
&
load time
Indirizzo
logico
Indirizzo
fisico
CPU
HW
MEM
DINAMICO:
Run time
(tempo di esecuzione)
Un HW particolare effettua il binding dinamico: la MMU
MMU: Rilocazione Dinamica
Il contenuto di un particolare registro, il registro di rilocazione, viene aggiunto ad ogni
indirizzo riferito da un processo. Con l'indirizzo risultante si accede alla memoria:
relocation
register
14000
logical
address
physical
address
CPU
+
memory
346
14346
MMU
Caricamento: Statico o Dinamico?
Il caricamento (loading) può essere:
Statico: l'intero codice viene caricato in memoria per procedere all'esecuzione
Dinamico: cerca di migliorare l'utilizzo della memoria centrale:
- i moduli di codice vengono caricati al momento del loro primo
utilizzo
- conseguenza: le parti del codice non utilizzate non vengono
caricate, "risparmiando" memoria
- tecnica utile quando il codice è suddivisibile in parti che
gestiscono "casi specifici" (es. error handling)
Solitamente il caricamento dinamico richiede un intervento minimo da parte del S.O .:
si può realizzare a livello di linguaggio/programma
Collegamento: Statico o Dinamico?
Statico: tutti i riferimenti del codice sono definiti prima dell'avvio
dell'esecuzione. L'immagine del processo contiene tutto il codice
delle eventuali librerie utilizzate
Dinamico: è utilizzato solitamente per il codice delle librerie
- il linking viene posticipato al tempo di esecuzione
- conseguenza: il codice del programma non include il codice delle
librerie, ma solo dei riferimenti (stub)
- lo stub indica dove trovare il codice della libreria alla prima
necessità. Caricata la libreria lo stub viene rimpiazzato con la sua
locazione in memoria
- tutti i processi eseguono una istanza dello stesso codice di librerie
(di cui una sola copia è in memoria)
Il linking dinamico richiede un intervento essenziale da parte del S.O. (può interessare
spazi di memoria di processi diversi)
Swapping
Con la tecnica dello swapping, un processo può essere rimosso dalla memoria
principale, swapped out, e spostato in memoria secondaria. In seguito potrà essere
ricaricato in memoria principale, swapped in.
operating
system
process
1
swap out
2
swap in
process
P2
user
space
backing store
main memory
Dettagli sullo Swapping
- il tempo di swapping è proporzionale all'ammontare dei dati trasferiti
- ha senso se c'è multiprogrammazione (altrimenti si dovrebbe attivare ad ogni
context switch)
- di norma è necessario il supporto al binding dinamico per poter ri-locare i
processi quando vengono swapped-in ...
- ... infatti con il binding statico si deve ricaricare il processo sempre nella stessa
posizione
- i moderni S.O. contemplano lo swapping, in una qualche forma
Swapping: Tecniche Alternative
La tecnica di swapping descritta finora è anche detta swapping standard
Alcuni sistemi operativi adottano forme diverse di swapping:
- per evitare di trasferire l'intero processo (costoso in termini di tempo impiegato)
molti moderni S.O. effettuano lo swapping limitatamente a porzioni del
processo, dette pagine. La tecnica è connessa all'uso della paginazione, che
vedremo in seguito
- I sistemi operativi dei dispositivi mobili (ad esempio Android e iOS) non
operano swapping di processi. Ciò per vari motivi:
- la memoria di solito è flash e non sono presenti HD
- la memoria flash presenta un limite al numero di scritture, lo swapping
(tradizionale o solo di pagine) degraderebbe la memoria
- invece di effettuare swapping, se c'è necessità di liberare memoria, il S.O.
richiede alla app di rinunciare a parte della memoria allocata; oppure sottrae alla
app le pagine di memoria non modificate; oppure (come ultima opzione) termina
la app
Considerazioni sulla Gestione della Memoria
- in presenza di multiprogrammazione non è praticamente possibile fissare
staticamente la locazione ove un processo verrà caricato
- la presenza di swapping impone l'impiego di rilocazione dinamica
Sono necessarie delle tecniche specifiche per la gestione della memoria principale.
Nel caso si preveda che il programma sia interamente caricato in memoria principale,
le principali tecniche sono:
- Allocazione contigua
- Paginazione
- Segmentazione
- Segmentazione paginata
Tecniche di Gestione della Memoria
Allocazione Contigua
Allocazione Contigua
- I processi sono allocati in memoria in posizioni contigue: ogni processo occupa
una unica sezione di memoria di dimensione pari alla sua dimensione
- La memoria viene suddivisa in due "zone":
- una utilizzata dal S.O. (solitamente la zona "bassa" contenente il vettore delle
interruzioni)
- una dedicata ai processi utente
- La memoria viene quindi suddivisa in partizioni distinte: ognuna accoglierà un
processo.
Due varianti di questo approccio:
- Allocazione contigua con partizioni fisse
- Allocazione contigua con partizioni variabili
Allocazione Contigua: Partizioni Fisse
- La memoria viene suddivisa in un insieme di
partizioni di dimensioni prefissate (non
necessariamente tutte uguali)
- la scelta di quante partizioni e delle
dimensioni viene fatta una volta per tutte
- un processo viene caricato in una partizione
in grado di contenerlo interamente
Operating System
8M
Operating System
8M
2 M
8M
4M
6M
8M
8M
8M
8 M
12 M
8 M
8 M
16 M
8M
(a) Equal-size partitions
(b) Unequal-size partitions
Allocazione Contigua: Partizioni Fisse e Allocazione della Memoria
Il long-term scheduling determina la partizione in cui caricare un processo
Due tecniche base:
- una coda per ciascuna
partizione
- una coda singola
Operating
System
Operating
System
New
Processes
New
Processes
(a) One process queue per partition
(b) Single process queue
- Una coda per partizione.
- alla sua creazione un processo viene assegnato coda relativa alla partizione più
piccola tra quelle in grado di contenerlo
- limitata flessibilità: ci possono essere processi in coda e partizioni vuote non
utilizzate (perchè relative ad altre code)
- Coda singola. Il processo viene allocato nella partizione libera più piccola tra quelle in
grado di contenerlo (se non esiste: swap-out)