S I S T E MI
OPERATIVI
Sistemi operativi
Processi 2
Componenti di un sistema operativo 3
Tipologie di un sistema operativo 4
Nucleo di un sistema operativo
Caratteristiche dei processi 7
Stati dei processi 8
Scheduler e Dispatcher 10
Competizione e cooperazione 14
Gestione dell’I/O
Classificazione dei device 22
Gestione dei dischi 24
Gestione della memoria centrale
Allocazione contigua 31
Allocazione non contigua 33
Memoria virtuale 34
Gestione del File System
Directory 43
Allocazione 48
FAT 52
Glossario
A.C. Neve – Sistemi operativi 3.0
1
Sistemi Operativi
Un sistema operativo è un complesso insieme di programmi che consente il buon funzionamento di
un elaboratore. Il sistema operativo agisce da intermediario fra l’utente e la struttura fisica del
calcolatore. Esso ha il compito di gestire e controllare le risorse del sistema (l’hardware ed il
software) fornendo un ambiente di lavoro di semplice uso, stabile ed indipendente dall’hardware.
Il sistema operativo deve quindi realizzare politiche di gestione delle risorse hardware al fine di:
1. regolamentare l’impiego delle risorse evitando conflitti di accesso,
2. scegliere i criteri con cui assegnare una risorsa a fronte di più richieste contemporanee, a
causa della limitata disponibilità di risorse,
3. nascondere all’utente i dettagli hardware legati al particolare dispositivo.
Processi
Un processo rappresenta un programma in esecuzione.
L’esecuzione di un programma richiede un flusso di varie operazioni compiute dalla CPU nelle
quali, l’esecuzione delle specifiche istruzioni del programma sono solo una parte.
Un processo comprende anche tutte le risorse necessarie per la sua esecuzione (programm counter,
stack, dati, device, ecc.)
Molto spesso, l’esecuzione di un programma richiede l’utilizzo di più risorse che vengono attivate
per mezzo di altri processi. L’attivazione di questi altri processi viene gestita dal sistema operativo.
Dato che la CPU può eseguire un solo processo per volta, sarà necessario sospendere il processo in
corso per attivare il nuovo. Questa commutazione, che è detta cambio di contesto, viene eseguita dal
sistema operativo. Il tempo di CPU è quindi in parte dedicato ai processi utente ed in parte ai
processi di sistema operativo. Il tempo necessario al cambio di contesto è detto overhead ed essendo
tempo sprecato, dovrà essere ridotto al minimo.
Programma (entità statica) e processo (entità dinamica) non sono quindi la stessa cosa.
Tutto il software attivo su un computer è organizzato in un insieme di processi sequenziali attivabili
uno per volta. Per poter sospendere e poi riattivare un processo è necessario poter ricordare le
condizioni in cui si trovava all’atto della sospensione. Questo insieme di informazioni è chiamato
stato del processo ed è costituito da tutte le informazioni necessarie per poter riprendere
l’esecuzione dopo una sospensione. Il PCB (Process Control Block) memorizza queste informazioni
Thread
Ci sono operazioni che vengono svolte sequenzialmente e che potrebbero essere svolte
concorrentemente riducendo così i tempi con la conseguente ottimizzazione delle prestazioni del
sistema. L'ideale sarebbe creare processi eseguibili in parallelo e che condividano risorse in comune
ma che siano strutturati in modo da rendere i tempi di cambiamento di contesto più brevi.
Si introduce quindi il concetto di thread. Il thread rappresenta l’unità di base di utilizzo della CPU.
Un thread è una parte di un processo che può essere eseguita contemporaneamente ad altri thread
riuscendo così ad aumentare lo pseudoparallelismo senza aumentare eccessivamente l’overhead.
Un processo è costituito da almeno un thread che viene creato automaticamente all’avvio del
processo. Un thread può creare altri thread e così via.
I thread generalmente condividono tutto il codice del processo e lo spazio dei dati delle variabili,
ciò implica che la commutazione tra i thread risulterà molto veloce.
Un processo può creare dei thread ma non viceversa.
A.C. Neve – Sistemi operativi 3.0
2
Componenti di un SO
Gestore dei processi
Gestore degli I/O
Gestore della memoria
Gestore del file system
Gestore del networking
Il gestore dei processi si occupa di tutte le operazioni che coinvolgono i
processi. Un processo è un programma in esecuzione che per svolgere i
suoi compiti necessita di alcune risorse (CPU, memoria, file, dispositivi
di I/O). Il sistema operativo si occupa di mandare un programma in
esecuzione ed allocare le risorse utilizzate. Il sistema operativo quindi:
- crea e cancella i processi utente e di sistema,
- sospende e ripristina i processi,
- fornisce meccanismi per la sincronizzazione dei processi,
- fornisce meccanismi per la comunicazione tra i processi,
- fornisce meccanismi per la gestione dei deadlock.
Il sistema operativo deve nascondere all’utente le caratteristiche degli
specifici dispositivi hardware. Solo il driver dello specifico dispositivo
fisico conosce le caratteristiche dello stesso. Il sistema operativo realizza
un’astrazione dei dispositivi di I/O definendo un’interfaccia comune che
sfrutta i servizi dei driver (sottosistema I/O), che è composto da:
- componente per la gestione della memoria di un buffering,
latching e spooling,
- un’interfaccia generale per i driver dei dispositivi,
- i driver per gli specifici dispositivi.
Prima di eseguire un programma deve essergli allocata la memoria: il
programma deve essere associato a indirizzi assoluti e caricato in
memoria; tali indirizzi vengono generati dalla CPU.
Se ci sono più programmi che richiedono l’elaborazione occorre scegliere
quale mandare in esecuzione tenendo conto della memoria totale
disponibile e di quella richiesta.
Quando il programma termina il sistema operativo gli revoca la memoria
rendendola disponibile per l’esecuzione di altri programmi.
In particolare deve:
- tener traccia della memoria disponibile,
- selezionare i processi da caricare in memoria,
- assegnare e revocare la memoria ai processi,
- memorizzare per ogni processo la memoria allocata
I dispositivi di memorizzazione secondaria sono molteplici ed ognuno ha
proprie caratteristiche ed una organizzazione fisica diversa. Il sistema
operativo si occupa di semplificare i compiti dell’utente e traduce le sue
richieste per i dispositivi fisici.
Riguardo alla gestione dei file è responsabile di:
- creare e cancellare file e directory,
- fornire all’utente le funzioni fondamentali per la manipolazione di
file e directory,
- creare una associazione tra i file e i corrispondenti dispositivi di
memoria secondaria,
- mantenere un back up dei file su dispositivi di memorizzazione
stabili, non volatili.
In un sistema distribuito le unità di elaborazione sono collegate da una
rete di comunicazione. I calcolatori che ne fanno parte condividono le
risorse.
Quindi il sistema operativo deve gestire l’accesso a queste ultime e
gestire la comunicazione con processi remoti. Per far questo è dotato di
A.C. Neve – Sistemi operativi 3.0
3
Gestore della sicurezza
Interfaccia utenti
un dispositivo di interfaccia con la rete (dispositivi di I/O) e sviluppa la
parte dei protocolli di rete nel sistema operativo stesso.
Se un sistema di calcolo ha più utenti e consente che più processi siano
eseguiti in modo concorrente, i diversi processi devono essere protetti
dall’attività di altri processi e gli utenti possono accedere solo alle risorse
di cui dispongono i permessi.
Per questo motivo esistono meccanismi che assicurano che i file, i
segmenti della memoria, la CPU e altre risorse possano essere controllate
solo dai processi che hanno ricevuto l’autorizzazione dal sistema
operativo. E’ compito del sistema operativo amministrare la sicurezza
del sistema nel modo più opportuno.
La UI oppure GUI è l’interfaccia tra il sistema operativo e l’utente
trasformando le indicazioni dell’utente in comandi per il sistema
operativo senza doverne gestire tutti i dettagli.
Tipologie dei sistemi operativi
batch
single user single task
single user multi tasking
multi user
I sistemi di tipo batch prevedono l’esecuzione di un programma alla
volta sempre residente nella memoria: il calcolatore legge un
programma, lo carica in memoria e lo esegue fino alla fine o fino ad un
errore, poi stampa il risultato. Quindi passa ad un nuovo programma:
lettura, memorizzazione, esecuzione, stampa. E così via per tutti i
programmi.
Il sistema operativo utilizzato è molto semplice: non c’è interazione tra
programma e utente, non si devono prendere decisioni su come allocare
le risorse, né scegliere il successivo programma da eseguire.
Questi sistemi sono poco efficienti e la CPU è spesso inattiva.
Prevede un unico utente ed una sola attività in esecuzione.
Prevede un unico utente ma la possibilità di eseguire più attività
“contemporaneamente”, sono anche detti multiprogrammati.
La multiprogrammazione consente di aumentare l’utilizzo della CPU
organizzando i lavori in modo tale da mantenerla in continua attività.
In questi sistemi sono presenti contemporaneamente in memoria centrale
diversi programmi, uno dei quali viene selezionato e mandato in
esecuzione. Il processo creato mantiene il controllo della CPU finché
non si arresta perché termina o perché in attesa di qualche evento come
il completamento di una operazione di I/O. A questo punto la CPU
invece di rimanere inattiva viene assegnata ad un altro processo presente
in memoria. Se il primo processo non era terminato, quando torna pronto
riprende il controllo della CPU continuando la sua esecuzione.
La multiprogrammazione ottimizza l’utilizzo delle risorse, in particolare
della CPU che viene commutata tra i processi in esecuzione per ridurre i
tempi morti
Una variante dei sistemi multiprogrammati è rappresentata dai sistemi
interattivi (multi user) in cui la CPU viene commutata più velocemente
per permettere all’utente di interagire con il programma.
Per fare questo (e per dare all’utente l’impressione che l’esecuzione dei
programmi sia parallela) si introduce il meccanismo del timesharing
(ripartizione del tempo) secondo il quale si commuta la CPU tra i diversi
programmi per un quanto di tempo T. Ogni programma viene quindi
A.C. Neve – Sistemi operativi 3.0
4
paralleli
reti di calcolatori
cluster
real time
eseguito per un tempo minore o uguale a T. Se non ci sono errori che
sospendono l’esecuzione prima che sia trascorso il tempo T, il
programma viene bloccato ed il controllo della CPU passa ad un altro
programma, mentre il precedente viene accodato in una apposita lista.
Il tempo impiegato dal sistema operativo per trasferire il controllo da un
programma ad un altro è detto overhead e deve essere limitato il più
possibile. La scelta dell’intervallo di tempo T dipende dal numero di
programmi che devono essere eseguiti: maggiore è il numero di
programmi, minore è T. Se T è sufficientemente piccolo l’utente non si
accorge del cambiamento di esecuzione.
I sistemi paralleli sono dotati di più unità di elaborazione, per questo
sono chiamati anche sistemi multiprocesso.
I processori che compongono questo tipo di sistemi sono strettamente
accoppiati: condividono risorse hardware e tipicamente hanno lo spazio
di memoria in comune. Le operazioni sono eseguite in parallelo su più
processi; quindi se una solita operazione deve essere eseguita su dati
diversi, sfruttando la presenza di più CPU, vengono mandati in
esecuzione contemporaneamente processi diversi.
Offrono maggiore efficienze e maggiore affidabilità.
In una rete di calcolatori il sistema è costituito da più processori come
accade nei sistemi paralleli, ma a differenza di questi ultimi, i processori
sono dislocati a distanze elevate uno dall’altro. Inoltre nei sistemi
paralleli lo spazio di memoria è generalmente in comune (in realtà
dipende dalla struttura del sistema) , quindi i diversi processori non sono
completamente autonomi. Nelle reti invece i nodi sono indipendenti e
possono lavorare da soli.
SISTEMI DI RETE: il sistema operativo è supportato da moduli per il
networking e da programmi per la comunicazione in rete con altri
calcolatori simili. L’utente ha visione di tutte le risorse disponibili, può
quindi accedere a macchine remote e copiare file da una macchina
all’altra. Ogni macchina ha il proprio sistema operativo locale.
SISTEMI DISTRIBUITI: il sistema operativo così organizzato fa apparire
la rete come un unico sistema monoprocessore: l’utente manda in
esecuzione un programma su una macchina; sarà poi il sistema operativo
a decidere su quale macchina verrà realmente eseguito (di solito sceglie
il processore meno occupato), quindi l’utente non sa quali risorse
vengono utilizzate. Risultano più complessi.
I sistemi cluster hanno alcune caratteristiche simili ai sistemi paralleli e
altre simili alle reti: sono composti da più nodi autonomi che
condividono le risorse (per esempio condividono dispositivi di memoria
secondaria – dischi). Questi sistemi sono utilizzati per garantire
affidabilità e disponibilità: devono produrre grosse prestazione o essere
sempre disponibili (server web). Le richieste vengono gestite da un
controllore e smistate ai nodi. Le richieste di uno stesso utente possono
essere gestite tutte da uno stesso nodo o da nodi diversi a seconda della
politica scelta dal controllore.
I sistemi di elaborazione in tempo reale si usano quando è necessario
fissare rigidi vincoli temporali per le operazioni della CPU o per il flusso
di dati. In questi sistemi tutte le elaborazioni devono essere eseguite
entro un limite massimo temporale, altrimenti i risultati non risultano più
attendibili.
A.C. Neve – Sistemi operativi 3.0
5
embedded
mobile
transaction processing
HARD REAL-TIME: i compiti critici devono essere completati in un
dato intervallo di tempo; il mancato rispetto del vincolo temporale
produce effetti catastrofici (la definizione di effetti catastrofici è
relativa).
SOFT REAL-TIME: il mancato rispetto del vincolo temporale non ha
conseguenze disastrose (per es. sistemi multimediali, realtà virtuale).
I sistemi embedded vengono eseguiti su calcolatori che controllano
dispositivi che non sono generalmente veri e propri sistemi di
elaborazione (per esempio televisori, lavatrici, forni a microonde,
telefoni cellulari). Il sistema in questo caso è ottimizzato per la specifica
applicazione, per questo è in contraddizione con i general purpuse i quali
sono progettati per svolgere funzioni diverse.
Il sistema operativo risulta quindi semplice: si limita ad interpretare i
comandi, gestire la memorizzazione dei dati sui file e attivare i
programmi applicativi. I sistemi embedded spesso hanno esigenze di
real-time, ma hanno anche problemi di limitazioni di memoria (non c’è
la virtualizzazione delle risorse) e di alimentazione
I sistemi mobili comprendono palmari, notebook, cellulari che possono
connettersi alle reti.
La dimensione ridotta di questi dispositivi rende il sistema che li realizza
particolare:
Per ridurre al minimo le dimensioni dei portatili si utilizzano risorse
hardware inferiori ai normali pc.
La memoria disponibile è limitata ed a volte non è realizzata la
virtualizzazione della memoria; il sistema operativo deve quindi gestire
la memoria in modo efficiente.
L’unità di elaborazione è lenta, perché una maggiore frequenza della
CPU comporta un maggior consumo. Quindi il sistema risulta avere una
minor potenza di calcolo.
Perché questi dispositivi siano portatili le batterie devono essere leggere
e non alimentate da rete.
Tutte le risorse sono progettate in modo da consumare il meno possibile
e utilizzate nel modo più efficiente, compatibilmente al minor consumo.
Per transazione si intende una operazione le cui varie fasi devono
avvenire tutte con successo altrimenti l’operazione stessa deve essere
annullata in tutte le sue fasi. Un esempio sono le transazioni bancarie.
Si evidenzia il fatto che, la classificazione ora proposta non definisce le tipologie in modo
mutuamente esclusivo in quanto possono esistere sistemi operativi le cui caratteristiche soddisfano
contemporaneamente più di una delle tipologie esposte.
A.C. Neve – Sistemi operativi 3.0
6
Caratteristiche dei processi
Locality
Uno degli aspetti più apprezzati di un processo è la velocità di accesso al codice eseguibile e la
relativa disponibilità dello stesso nei vari livelli di memoria.
Il concetto di locality si riferisce alla possibilità di prevedere il futuro punto di accesso in memoria.
Spatial Locality: se un programma accede ad una particolare locazione di memoria, la probabilità
che acceda, in un prossimo futuro, a locazioni vicine è molto elevata. Ciò è giustificato
dall’esecuzione sequenziale e dall’utilizzo di strutture dati di tipo vettoriale.
Temporal Locality: se un programma accede ad una particolare locazione di memoria, è probabile
che vi acceda nuovamente in un prossimo futuro. Ciò è giustificato dall’uso frequente nei
programmi di tecniche iterative
Working set: è la generalizzazione del concetto di locality in ambito di gestione della memoria. Il
working set è l’insieme delle pagine di memoria utilizzate nelle ultime unità temporali di lavoro.
Boundless
Un processo che fa uso frequente di I/O si dice I/O bound.
Un processo che fa molta computazione e poco I/O si dice CPU bound.
La fase di utilizzo della CPU si dice CPU burst e quella di I/O invece I/O burst.
Un processo si dice che è in fase di I/O bound se esegue una operazione di I/O in un tempo uguale o
inferiore a 10 unità di tempo.
Un processo si dice in fase di CPU bound se esegue 100 o più istruzioni senza operazioni di I/O.
Un processo è in fase neutrale se non rientra nelle due precedenti.
La conoscenza della tipologia di bound è importante nei sistemi multiprocesso per poter assegnare
la CPU o l’I/O ai diversi processi in modo da migliorare l’impiego delle risorse e l’efficienza.
Registri e memoria
L’accesso ai dati per l’esecuzione delle istruzioni può essere effettuato dai registri o dalla memoria
RAM. Nel caso dei registri la velocità di elaborazione è massima.
Un processo si dice che è in fase di register intensive se esegue 100 o più istruzioni senza accedere
alla RAM mentre è in fase RAM intensive se accede intensivamente alla RAM
Foreground/background
I processi foreground sono gestiti e quindi interagiscono con gli utenti mentre i processi
background sono in stato di sospensione pronti però ad attivarsi per fornire servizi.
A.C. Neve – Sistemi operativi 3.0
7
Stati dei processi
Process Control Block
In un sistema operativo, un processo è rappresentato da una struttura dati detta Process Control
Block (PCB) o descrittore del processo.
stack del
processo
puntatore
numero del processo
contatore di programma
registri
limiti di memoria
elenco dei file aperti
...
Il PCB contiene le seguenti informazioni:
program counter
area di stack per il salvataggio dei registri di stato e general purpuse
stato di avanzamento del processo
identificatore del processo
puntatori ai processi dipendenti
livello di priorità
informazioni per il memory management
informazioni per lo scheduling del processo
informazioni sull’accounting del processo
informazioni sull’I/O del processo
Il PCB è quindi la struttura dati mediante la quale il sistema operativo gestisce un processo e ne
rappresenta lo stato globale.
Stati di un processo
Lo stato di un processo consiste in tutte le informazioni necessarie per riprendere l’esecuzione dopo
una temporanea sospensione.
Un processo si evolve attraverso una serie di stati di avanzamento discreti:
• Stato iniziale: NEW. Quando si manda in esecuzione un programma si crea un processo
ed il relativo descrittore. Il programma creato si trova nello stato new. I campi del PCB vengono
inizializzati con dei valori iniziali appropriati e il processo viene inserito in una coda. Si crea anche
un identificatore (PID) che serve al sistema operativo per identificare quel processo. Il processo
creato non ha ancora la memoria a sua disposizione, quindi non è ancora in esecuzione. Esistono
più processi in stato new raggruppati in una lista da cui verranno scelti in base alla politica dello
schedulatore. Il programma verrà poi selezionato per l’assegnazione della memoria.
• Stato READY. Quando un processo che si trova nello stato iniziale viene scelto dallo
schedulatore, gli viene associata la memoria e passa nello stato ready, in attesa di essere assegnato
ad una unità di elaborazione (il processo viene inserito nella lista dei processi pronti). Oltre a
modificare opportunamente il campo stato, operazione che avviene ad ogni cambiamento di stato,
nel descrittore si inseriscono anche le informazioni sulla memoria allocata. Il processo è pronto per
essere mandato in esecuzione e possiede tutte le risorse necessarie tranne la CPU.
• Stato RUNNING. Il passaggio dallo stato ready allo stato running avviene quando l’unità
di elaborazione è libera. Un processo passa dallo stato ready allo stato running quando un’unità di
elaborazione esegue le istruzioni del relativo programma. Poiché esistono più di un processo nello
stato ready, la selezione del processo da mandare in esecuzione avviene in base alla politica adottata
dal sistema. Una componente del sistema operativo (dispatcher) commuta il contesto: copia i valori
attuali della CPU nel descrittore del processo uscente e carica i registri della CPU con i nuovi valori
prelevati dal descrittore del processo entrante. Il processo uscente viene inserito nella lista dei
processi pronti. Da questo stato si può tornare allo stato ready per effetto di una interruzione (per
esempio nei sistema timesharing trascorso il quanto temporale a disposizione di un processo si ha
un cambiamento di contesto), oppure se il processo richiede operazioni di I/O si passa allo stato di
waiting, infine se il processo è terminato passa nello stato terminate.
A.C. Neve – Sistemi operativi 3.0
8
• Stato WAITING. Quando i processi richiedono operazioni che non coinvolgono l’utilizzo
della CPU (per esempio una operazione di I/O) vengono messi nello stato waiting in attesa che
l’operazione termini (intanto la CPU passa ad eseguire un altro processo); quindi torna in READY
in attesa che sia di nuovo il suo turno. Un processo può andare in stato WAITING anche per
problemi diversi dalle chiamate di I/O, per esempio per attese di particolari eventi.
• Stato TERMINATE. Il processo può terminare in modo anomalo, mentre è in stato
RUNNING, o normale. Il processore aggiunge alla fine del programma una chiamata di sistema che
lo fa terminare. Tutte le risorse vengono liberate, eventuali file aperti vengono chiusi dal sistema
operativo, la memoria allocata torna libera e tutte le risorse revocate. Il descrittore di processo viene
liberato e messo in una coda in attesa di essere allocato per un altro processo.
ammesso
uscita
new
terminate
interruzione
ready
running
dispatch
completamento di un
I/O o verificarsi di un
evento
attesa di un I/O o
di un evento
waiting
Operazioni sui processi
CREATE
I principali eventi che possono determinare la creazione di un processo sono:
avviamento ed inizializzazione del sistema
un processo in esecuzione invoca una system call creation
un utente richiede la creazione di un nuovo processo
FORK
Un processo può creare un nuovo processo per mezzo della procedura fork.
Il processo creatore viene detto parent mentre quello creato è detto child. In questa
gerarchia, un parent può avere più child ma un child può avere un solo parent.
JOIN
La join è usata per unificare due sequenze di codice sdoppiate da una fork. E’ utile
al processo parent per potersi sincronizzare con il processo child.
ABORT
Determina la terminazione forzata di un processo ed è usata per rimuovere un
processo malfunzionante. Crea anche delle informazioni sui motivi del
malfunzionamento.
SUSPEND
Un processo può sospendere se stesso o un altro processo secondo le priorità.
RESUME
Riattiva un processo sospeso
DELAY
Gestisce i time out cioè il tempo di attesa di un certo evento, scaduto il quale, il
processo riprende il controllo.
TERMINATE Rappresenta la conclusione di un processo. Questo può avvenire in più modi:
Normal exit: quando il processo si conclude regolarmente
Error exit: quando si verifica un errore parametrico
Fatal error: quando si verifica un grave errore che impedisce la prosecuzione
Killed: quando un altro processo ne chiede la conclusione
A.C. Neve – Sistemi operativi 3.0
9
Thread
Si è già detto che un thread rappresenta l’unità di base di utilizzo della CPU che viene individuato e
schedulato per l’esecuzione da parte della CPU.
Un thread è caratterizzato dal Program Counter, un insieme di registri ed una area di stack.
Il thread è quindi più leggero da gestire rispetto ai normali processi, in particolare riduce
l’overhead.
Nella programmazione multithread, ogni processo è composto da singoli thread in esecuzione per
ogni attività concorrente e che condividono codice, dati e file. Ogni singolo thread contiene i
registri e lo stack che definiscono l'attività caratteristica di quel thread. Con l’utilizzo della
programmazione a multithread i tempi di commutazione di contesto si riducono notevolmente.
Quando si commuta il contesto, il salvataggio dello stato del vecchio thread ed il caricamento dello
stato del nuovo thread (all’interno di uno stesso processo) riguarda solo i registri e lo stack dato gli
altri dati sono in comune. I vantaggi della programmazione multithread sono:
•
•
•
•
Tempi di risposta più brevi. Un programma può continuare la sua elaborazione anche quando
un thread che lo compone è bloccato o sta eseguendo una operazione particolarmente lunga.
Ottimizzazione della condivisione delle risorse. Un’applicazione può avere molti thread di
attività diverse, tutti nello stesso spazio di indirizzamento, grazie alla condivisione del codice.
Aumento del grado di parallelismo nei sistemi con più unità di elaborazione. Nelle architetture
a singola CPU i thread si scambiano il controllo della CPU molto velocemente, creando
l’illusione di una esecuzione parallela, in realtà si esegue un thread alla volta.
Tempi più brevi derivanti dalla condivisione delle risorse dei thread. Nei thread, i tempi di
cambio di contesto sono inferiori rispetto alla creazione ed allocazione della memoria ai
processi. Quindi è conveniente creare un solo processo composto da tanti thread invece di
creare tanti processi indipendenti.
Scheduler e Dispatcher
Si intende per scheduling il criterio di individuazione, di uno tra più richiedenti, al quale assegnare
l’esecuzione di un sevizio.
Il concetto di scheduling risulta essere alquanto ampio e pertanto ne vengono definiti tre livelli:
Long Term Scheduling (LTS): Regola l’accesso dei programmi al sistema per la loro esecuzione e
quindi controlla il grado di multiprogrammazione per cercare di massimizzarlo. Suddivide
i processi in gruppi bilanciati nell’uso di CPU ed I/O per poi sottoporli all’STS.
Medium Term Scheduling (MTS): Riguarda solo una parte dei processi che vengono mantenuti in
memoria mentre i rimanenti sono trasferiti su disco. Vengono rimossi i processi che sono
rimasti in memoria un tempo sufficientemente lungo per caricare un nuovo insieme di
processi dal disco. Si tratta di un livello connesso alla gestione della memoria virtuale.
Short Term Scheduling (STS): è il normale scheduler di CPU dei processi residenti in memoria ed è
quello che sarà in seguito preso in considerazione.
Scheduler
Il compito dello scheduler è quello di stabilire quale sarà il successivo processo al quale assegnare
la CPU per l’esecuzione. La scelta avviene per mezzo di opportuni algoritmi di schedulazione.
L’effettiva assegnazione della CPU al processo prescelto è effettuata dal dispatcher .
Scheduler e dispatcher operano sulle code di attesa dei processi.
A.C. Neve – Sistemi operativi 3.0
10
Dispatcher
Il dispatcher è un modulo del sistema operativo che passa effettivamente il controllo della CPU ai
processi scelti dallo scheduler. Il dispatcher effettua il cambio di contesto (context switch)
assegnando la CPU ad un processo di un utente precedentemente sospeso e riprendendone
l’esecuzione dal punto dove era stato sospeso.
Il tempo necessario per sospendere un processo ed avviare il successivo è detto tempo di latenza.
Context switch
Per assicurare ad ogni processo un equo utilizzo della CPU, il sistema operativo usa un Real Time
Clock hardware che genera periodicamente un interrupt consentendo una regolare schedulazione dei
processi presenti in memoria.
Quando la CPU viene assegnata ad un nuovo processo si ha un context switching.
I valori dei registri sono salvati nel PCB del processo che viene sospeso ed i registri sono caricati
con i valori del nuovo processo. Queste operazioni richiedono un tempo non trascurabile mentre per
i thread questa operazione è molto più veloce in quanto è relativa ad una minore quantità di
informazioni da commutare.
Processo P0
in esecuzione
Sistema Operativo
Processo P1
interruzione o chiamata del sistema
Salva stato in PCB0
scheduler
inattivo
Ripristina stato da PCB1
inattivo
in esecuzione
interruzione o chiamata del sistema
Salva stato in PCB1
scheduler
Ripristina stato da PCB0
inattivo
in esecuzione
Criteri di scheduling
L’obbiettivo dello scheduling è quello di ottimizzare le prestazioni dell’intero sistema ed in modo
particolare quello della CPU. Si definiscono quindi alcuni indici di prestazione in base ai quali si
determina il comportamento di un sistema.
•
•
Fairness: lo scheduler deve essere imparziale. Ogni processo deve ricevere la sua equa parte di
tempo di CPU e nessun processo deve attendere indefinitamente. Equità però non vuol dire
uguaglianza ma assegnazione di tempo proporzionale all’importanza ed alla priorità.
Policy Enforcement: lo scheduler deve assicurare che le politiche del sistema siano sempre
garantite e cioè che i processi ad alta priorità siano sempre eseguiti.
A.C. Neve – Sistemi operativi 3.0
11
•
•
•
•
•
Efficiency: lo scheduler deve sempre mantenere le risorse del sistema impegnate al 100%.
Response time: è il tempo che intercorre tra l’istante in cui viene generata una richiesta e
l’istante di inizio dell’asservimento. Il suo valore deve essere il più piccolo possibile.
Tempo di attesa: rappresenta la somma dei tempi che i processi passano in coda.
Throughput: numero dei processi elaborati per unità di tempo.
Overhead: percentuale di tempo di CPU usata dallo scheduler e dal dispatcher.
Scheduling preemptive e non preemptive
Gli algoritmi di schedulazione possono essere divisi in due categorie: preemptive e non preemptive
(con prerilascio e senza prerilascio oppure interrompente e non interrompente).
Lo scheduling si dice non preemptive se, una volta assegnata la CPU ad processo, questa non può
essere tolta al processo in base ad eventi esterni al processo stesso. In questo caso, il context switch
si potrà attivate solo se il processo termina o si blocca.
Lo scheduling si dice preemptive quando è possibile togliere la CPU ad un processo in relazione ad
eventi esterni al processo stesso e cioè può essere sospeso anche se dispone di tutte la risorse
necessarie, per es. l’arrivo di un processo con priorità più elevata.
Algoritmi di schedulazione
First Come First Served (FCFS)
Viene anche detto FIFO (First In First Out) ed utilizza la politica secondo la quale il primo arrivato
è il primo servito per cui la coda dei processi viene riempita secondo il tempo di arrivo.
Questo algoritmo è il più utilizzato, soprattutto per la sua semplicità realizzativa.
E’ un algoritmo non premptive per cui un processo al quale viene assegnata la CPU sarà eseguito
fino al suo completamento.
Si tratta di un algoritmo solo teoricamente equo in quanto un processo lungo e poco importante
potrebbe far attendere molto un processo breve ma importante.
I cambiamenti di contesto risultano molto veloci, ma i tempi medi di attesa in coda sono elevati.
Si consideri questo esempio:
Processo
P1
P2
P3
Burst Time
24
3
7
I tempi di attesa in coda risultano:
TP1 = 0;
TP2 = 24;
TP3 = 27;
Quindi il tempo medio di attesa sarà:
(TP1 + TP2 + TP3 )/3 = 17
Il valore ottenuto è elevato: l’algoritmo FCFS non fornisce grandi prestazioni dal punto di vista
dell’attesa dei processi in coda. Risulta quindi evidente che questo algoritmo non può essere
utilizzato nei sistemi real-time nei quali i processi richiedono risposte in tempi brevi.
Shortest Process Next First (SPNF)
Viene anche detto SJF (Shortest Job First) ed utilizza la politica secondo la quale il primo processo
ad essere eseguito è quello con minor tempo di esecuzione, cioè quello con CPU burst più breve. Se
nella coda dei processi pronti ci sono più processi con lo stesso CPU burst, tra questi si sceglie
secondo il criterio FCFS.
E’ un algoritmo non premptive per cui un processo al quale viene assegnata la CPU sarà eseguito
fino al suo completamento.
A.C. Neve – Sistemi operativi 3.0
12
La coda dei processi pronti è più complicata rispetto al caso precedente: non è organizzata più in
modalità FIFO, ma ordinata in base alla durata della successiva sequenza di operazioni di CPU.
L’estrazione risulta quindi immediata (se la coda è ordinata in modo decrescente si estrae il primo),
mentre l’inserimento è più complesso.
Con questo algoritmo si riducono i tempi medi di attesa dei processi in coda.
Considerando l’esempio precedente si ha che:
I tempi di attesa in coda risultano:
TP1 = 10;
TP2 = 0;
TP3 = 3;
Quindi il tempo medio di attesa sarà:
(TP1 + TP2 + TP3 )/3 = 4.5
Il risultato ottenuto è decisamente migliore di quello trovato con l’algoritmo FCFS.
Questo algoritmo è ottimale nel senso che rende minimo il tempo di attesa medio per un dato
insieme di processi in coda, ma non è realizzabile poiché non è nota a priori la durata della
successiva richiesta della CPU da parte di ogni singolo processo.
Questo algoritmo non è adeguato per ambienti time sharing al contrario per quelli batch.
Round Robin (RR)
I processi vengono eseguiti per un intervallo di tempo definito dal sistema (time slice).
Dopo tale intervallo il processo viene inserito in fondo alla coda dei processi pronti e la CPU viene
assegnata al prossimo processo prelevato dalla testa della coda. Se l’esecuzione del processo
termina prima dell’intervallo che gli è stato concesso, il processo stesso rilascia volontariamente la
CPU e lo scheduler seleziona il primo processo nella coda dei processi pronti. Quindi l’esecuzione
di ogni processo ha una durata massima definita dal quanto di tempo.
Questo algoritmo è di tipo preemptive e quindi adatto ai sistemi time sharing.
Questo tipo di scheduling è implementato tramite una coda circolare gestita in modalità FIFO e
garantisce che tutti i processi vadano in esecuzione, eliminando il problema della starvation (blocco
all’infinito).
Ogni volta che lo scheduler seleziona un processo dalla coda dei processi pronti (il primo) oltre ad
attivare il dispatcher per l’effettiva esecuzione del processo, imposta un timer inizializzato al quanto
di tempo specificato dal sistema operativo. Allo scadere del timer il sistema invia un segnare di
interruzione che blocca il processo attualmente in esecuzione, per assegnare la CPU ad un altro
processo.
La scelta del quanto di tempo da dedicare ad ogni processo è fondamentale: se il sistema sceglie un
intervallo troppo grande, l’algoritmo degenera nell’FCFS; se l’intervallo è troppo piccolo (al limite
0) degenera nel processor sharing, algoritmo a condivisione di processore, non realizzabile, nel
quale i diversi processi hanno l’impressione che ci siano n processori reali ognuno dei quali esegue
un processo diverso.
Il sistema operativo deve quindi scegliere un intervallo di tempo abbastanza piccolo da garantire
l’interattività, cioè deve fare in modo che i processi brevi vengano eseguiti velocemente, ma
abbastanza grande in modo da limitare l’overhead. Infatti se il quanto temporale è troppo piccolo il
sistema è rallentato dai numerosi cambi di contesto. Quindi l’intervallo deve essere molto maggiore
della latenza di dispatcher.
Shortest Remaining Time (SRT)
Questo algoritmo è la versione preemptive di SPNF ed è quindi idoneo al time sharing.
A.C. Neve – Sistemi operativi 3.0
13
L’algoritmo prevede che il successivo processo sia quello con minor tempo stimato per il
completamento. Con questo algoritmo si ha un overhead maggiore di SPNF in quanto è necessario
determinare ed aggiornare il tempo restante e gestire il preemptive.
Priority Scheduling
Questo algoritmo assegna ad ogni processo una priorità e viene mandato in esecuzione il processo a
priorità maggiore. Nel caso di processi con uguale priorità si opera con FCFS.
La priorità può essere assegnata dal sistema operativo o dal programmatore.
Nel primo caso si devono individuare delle proprietà misurabili mentre nel secondo caso la priorità
del processo è generalmente funzione dell’importanza del processo.
Il priority scheduling può essere sia preemptive che non preemptive.
Il problema fondamentale di questo algoritmo è la starvation (causato dalla priorità statica): l’attesa
di un processo in coda è indefinita poiché dipende dalla priorità di ogni singolo processo. In
particolare, l’esecuzione dei processi con bassa priorità può essere impedita da un flusso costante di
processi con priorità maggiore.
Poiché non c’è garanzia che un processo venga eseguito, si introduce il concetto di aging (si
introduce la priorità dinamica): periodicamente il sistema aumenta le priorità dei processi che sono
da molto tempo nella coda. In questo modo i processi con CPU burst brevi vengono eseguiti molto
più velocemente. Questo concetto è utilizzato in particolare nei sistemi interattivi.
Multilevel Queue Scheduling
L’algoritmo multilevel queue suddivide la coda in diverse code ognuna delle quali ha uno specifico
livello di priorità. I processi vengono assegnati staticamente ad una certa coda in base ad una
specifica tipologia di priorità. Ogni coda potrà poi avere un proprio algoritmo di schedulazione.
Naturalmente sarà poi necessario effettuare lo scheduling tra le varie code.
Competizione e cooperazione
Interferenza e sincronizzazione tra processi
I processi gestiti dal sistema operativo possono essere indipendenti o cooperanti.
I processi indipendenti non scambiano informazioni e l’esecuzione di tali processi non è condiziona
dall’esecuzione degli altri. Al contrario, due processi cooperanti si scambiano dati e le esecuzioni
sono influenzate l’una dall’altra.
La cooperazione è una caratteristica utile per diverse ragioni, in particolare:
• Consente la condivisione delle informazioni tra i processi cooperanti: per esempio se due utenti
devono condividere codice o dati, i processi devono cooperare.
• Aumenta il grado di parallelismo: esistono attività non sequenziali ma costituite da sottoattività
che possono essere svolte concorrentemente (per esempio scrittura, stampa e compilazione) e per
questo devono cooperare e sincronizzarsi.
• Modularizza il sistema: l’organizzazione a moduli semplifica il sistema. Ogni modulo può
corrispondere ad un processo o un thread, l’insieme dei quali costituisce un unico programma.
A.C. Neve – Sistemi operativi 3.0
14
Per permettere ai processi di cooperare il sistema operativo deve fornire dei meccanismi che
implementino la comunicazione e la sincronizzazione delle attività dei processi.
In un sistema in cui è ammessa la cooperazione, un processo può influenzare un altro processo in
esecuzione causando situazioni incoerenti o dati in uscita non attendibili.
I processi cooperanti possono condividere direttamente uno spazio logico di indirizzi, quindi il
sistema operativo deve implementare dei meccanismi che assicurino una ordinata esecuzione dei
processi stessi.
In ambiente multiprocesso i processi possono accedere a risorse condivise, se questi accessi non
sono opportunamente controllati si può determinare la contaminazione di dati o conflitti sulle
periferiche. E’ quindi necessario impedire che più di un processo possa, simultaneamente, leggere o
scrivere stessi dati o usare stesse periferiche.
Si parla di race condition quando più processi accedono e manipolano gli stessi dati
concorrentemente e l’esito varia a seconda dell’ordine con il quale sono avvenuti gli accessi. Per
evitare questa situazione occorre assicurare che un solo processo alla volta possa modificare la
variabile condivisa, condizione realizzabile tramite la sincronizzazione dei processi.
Per evitare il problema del race condition, i dati e le risorse condivise da più processi devono essere
usate e manipolate da un solo processo alla volta.
Ogni processo può quindi avere nel suo codice una sezione critica, cioè una parte di programma con
la quale può accedere a risorse comuni o modificare dati comuni ad altri processi. Ad un solo
processo per volta è consentito eseguire la propria sezione critica: l’esecuzione delle sezioni critiche
da parte dei processi deve essere mutuamente esclusiva nel tempo, cioè, quando un processo sta
eseguendo una parte di codice a sezione critica, nessun altro processo può fare altrettanto.
L’esecuzione delle sezioni critiche deve essere soggetta ad alcune regole:
Mutua esclusione: Se un processo sta eseguendo la sua sezione critica, nessun altro processo può
eseguire la sua sezione critica.
Progresso:
Se un processo è nella sua sezione critica ed esiste un processo che vuole entrare
nella sua sezione critica, l’esecuzione di quest’ultimo processo non può essere
rimandata all’infinito.
Attesa limitata: Se un processo ha chiesto di entrare nella sua sezione critica, il numero di volte
che si può rinviare questo accesso per concederlo ad altri processi deve essere
limitato.
E’ quindi evidente che, il primo processo che entra in sezione critica obbligherà tutti gli altri a
mettersi in coda di attesa. Esistono diversi metodi per attuare la mutua esclusione.
• Un primo metodo prevede la disabilitazione delle interruzioni quando un processo entra in
sezione critica e la riabilitazione quando ne esce. Può però essere pericoloso consentire ad un
processo utente la gestione delle interruzioni.
• Alcuni sistemi operativi hanno la possibilità di commutare dal modo multiprocesso al modo
single processing eliminando così la condizione di race condition. Anche in questo caso può
essere pericoloso consentire ad un processo utente la gestione di questa commutazione.
• Un altro metodo è il test and set che consiste nell’utilizzo di una variabile condivisa detta lock la
quale assume valore 1 quando c’è un processo in sezione critica altrimenti vale 0. Se un processo
vuole entrare in sezione critica testa la variabile lock: se vale 0 entra in sezione critica e la pone
ad 1 altrimenti continua a fare il test finché non la trova a 0. L’inefficienza di questo metodo
risiede nel fatto che il processo in coda spenderà molto tempo in un loop di test della variabile
lock.
A.C. Neve – Sistemi operativi 3.0
15
• Per risolvere il problema della sezione critica si usa uno strumento di sincronizzazione chiamato
semaforo. Un semaforo è una variabile intera il cui accesso può avvenire solo tramite due
operazioni atomiche standard, wait e signal, (atomico si riferisce al fatto che il flusso di
esecuzione delle due operazioni è indivisibile).
Inizialmente ogni semaforo viene inizializzato a 1. Quando un processo P1 vuole entrare nella
propria sezione critica, esegue la wait sul semaforo. Se nessun altro processo si trova nella
propria sezione critica, P1 continua la sua esecuzione dopo aver decrementato il valore del
semaforo. Se invece un altro processo P2 sta già eseguendo la sezione critica, P1 viene sospeso.
Quando il processo P2 esce dalla sua sezione critica esegue la signal che sblocca il processo P1.
Tutte queste operazioni devono essere fatte in un’unica indivisibile azione atomica in modo da
garantire che, una volta che un’operazione su un semaforo è cominciata, nessun altro processo
possa accedere al semaforo fino a che l’operazione non è completata. L’atomicità è
assolutamente essenziale per risolvere i problemi di sincronizzazione.
Il problema dello stallo
Si intende per sincronizzazione, quell’insieme di meccanismi che consentono ad un programma di
sospendersi in attesa di un segnale da parte di un altro processo che gli consenta di riprendere
l’avanzamento. Questo problema può essere risolto con l’uso dei semafori.
Un insieme di processi si trova in stato di stallo (deadlock) quando ognuno di essi è in attesa di un
evento generato da un altro processo che a sua volta si trova nella stessa condizione.
Nessuno dei processi può avanzare, nessuno può rilasciare le risorse e nessuno può essere posto
nello stato di ready.
In sintesi, il sistema è in deadlock poiché ogni processo dispone di una risorsa richiesta da un altro e
che non può rilasciare.
Anche le risorse possono essere preemptive e non preemptive:
una risorsa preemptive può essere sottratta al processo senza conseguenze
una risorsa non preemptive non può essere tolta al processo senza conseguenze negative, solo il
processo che la detiene può rilasciarla.
La riallocazione delle risorse potrebbe evitare il deadlock se le risorse fossero premptive.
Si definiscono quattro condizioni il cui simultaneo verificarsi determina un deadlock:
• Mutua esclusione: le risorse coinvolte non sono condivisibili, cioè solo un processo può usare la
risorsa e quindi si accede per mutua esclusione.
• Hold and wait: un processo richiede risorse ed è in attesa di altre risorse.
• Non preemptive: le risorse già allocate non sono preemptive (sia dal kernel che dal processo).
• Condizione di attesa circolare.
I processi del sistema formano quindi una lista circolare nella quale ogni processo è in attesa di una
risorsa posseduta dal successivo processo in lista.
Naturalmente il deadlock si può verificare solo in presenza di almeno due processi.
Esistono diversi modi per gestire il deadlock:
Rilevare e recuperare il deadlock: si usa un monitor che rileva l’esistenza di un deadlock ed
identifica i processi e le risorse coinvolti. Si disallocano temporaneamente le risorse dei processi
A.C. Neve – Sistemi operativi 3.0
16
in deadlock, si riportano questi processi in uno stato precedente al deadlock e si concludono
alcuni processi fino a rimuovere il deadlock. Si tratta però di una tecnica molto onerosa.
Evitare il deadlock mediante una oculata schedulazione delle risorse. L’algoritmo più noto è
quello del Banchiere così chiamato perché simile alla procedura che utilizzano i banchieri per
decidere se un prestito può essere o meno concesso senza rischi.
L’algoritmo si evolve quindi valutando se il soddisfacimento di una richiesta può condurre in
una stato sicuro o in deadlock ed in quest’ultimo caso ritarda la concessione della richiesta.
Prevenzione del deadlock: consiste nello schedulare le risorse in modo da evitare almeno una
delle quattro condizioni citate in precedenza.
Infine si può cercare di evitare la condizione di coda circolare forzando i processi a richiedere le
risorse in base ad un ordine crescente o decrescente. In questo modo il grafo di allocazione delle
risorse non potrà mai essere circolare.
La comunicazione
Spesso i processi hanno bisogno di comunicare tra loro. Nasce quindi la necessità che l’interazione
tra i processi avvenga in modo ben strutturato, mantenendo alta l’efficienza del sistema.
Per realizzare la comunicazione il sistema operativo deve implementare dei meccanismi che
consentano a un processo di passare informazioni ad un altro processo in modo sequenzialmente
corretto.
I processi concorrenti in esecuzione sotto il controllo del sistema operativo, possono essere
indipendenti o cooperanti. I processi indipendenti non scambiano informazioni e la loro esecuzione
non è condiziona dall’esecuzione degli altri. Due processi cooperanti al contrario si scambiano dati
e le esecuzioni sono influenzate l’una dall’altra.
Come già detto, la cooperazione è una caratteristica utile per diverse ragioni, in particolare:
• Consente la condivisione delle informazioni tra i processi cooperanti; per esempio se due utenti
devono condividere codice o dati i processi devono cooperare.
• Aumenta il grado di parallelismo; esistono attività non sequenziali ma costituite da sottoattività
che possono essere svolte concorrentemente e per questo devono cooperare e sincronizzarsi.
• Modularizza il sistema; l’organizzazione a moduli semplifica il sistema.
Per permettere ai processi di cooperare il sistema operativo deve fornire dei meccanismi che
implementino la comunicazione e la sincronizzazione delle attività dei processi. Esistono due
tecniche diverse per lo scambio di dati tra processi:
Modello a memoria comune: i due processi condividono una area di memoria comune tramite la
quale cooperano. Un processo scrive i dati nell’area comune, l’altro li legge.
Modello a scambio di messaggi: modello basato sullo scambio di messaggi tra processi. Il sistema
operativo provvede a recapitare i messaggi al destinatario implementando funzioni apposite a
questo scopo.
Per illustrare il modello a memoria comune si consideri il problema del produttore e del
consumatore che è un classico paradigma per processi cooperanti.
Un processo produttore produce informazioni che sono consumate da un processo consumatore.
A.C. Neve – Sistemi operativi 3.0
17
Per permettere un’esecuzione concorrente dei processi, occorre disporre di un vettore (buffer) in cui
il produttore può inserire un elemento mentre il consumatore ne sta prelevando un altro, facendo
attenzione alla capacità del vettore.
Nascono quindi due problemi:
Sincronizzazione tra i processi consumatore e produttore: il prelievo di un dato da parte del
consumatore deve avvenire dopo che il produttore lo ha inserito nel buffer.
Grandezza dell’area di memoria comune: se la memoria è illimitata, non ci sono limiti alla
grandezza del buffer e il produttore può sempre produrre dati; se la memoria è limitata il buffer avrà
una lunghezza massima e il produttore prima di inserire un dato nel buffer deve controllare che non
sia pieno. In entrambe le situazioni il consumatore, prima di prelevare un dato, deve sempre
controllare che il buffer non sia vuoto.
Un metodo alternativo con cui il sistema operativo può ottenere gli stessi risultati ottenuti con il
modello a memoria comune consiste nel fornire gli strumenti per la comunicazione tra processi,
realizzando ciò che comunemente prende il nome di sistema di comunicazione tra processi (IPC:
Inter Processing Communication).
Il modello IPC è particolarmente utile in un ambiente distribuito dove i processi comunicanti
possono risiedere in diversi calcolatori connessi da una rete.
Le due operazioni fondamentali che costituiscono il modello IPC e che permettono ai processi di
comunicare e sincronizzarsi senza condividere dati sono due:
- operazione
send(message),
- operazione
receive(message).
I processi che vogliono comunicare con il modello a scambio di messaggi possono farlo grazie alla
presenza di un canale di comunicazione, realizzabile in molti modi sia logicamente che fisicamente.
Per comunicare i processi devono disporre di un modo con cui riferirsi agli altri processi.
A secondo di come si implementa questa caratteristica si distinguono due differenti sistemi:
1. Comunicazione diretta.
Con la comunicazione diretta, ogni processo che intenda comunicare deve nominare esplicitamente
il ricevente o il trasmittente della comunicazione.
Nella comunicazione diretta il canale ha le seguenti caratteristiche:
• i canali sono creati automaticamente;
• tra ogni coppia di processi esiste un canale
• i processi per comunicare devono conoscere solo la reciproca identità;
• un canale è associato esattamente a due processi;
• il canale può essere unidirezionale o, più spesso, bidirezionale.
2. Comunicazione indiretta.
I messaggi vengono inviati a delle mailbox (chiamati anche porte) e da essi ricevuti. La mailbox è
un oggetto in cui i processi possono introdurre e da cui possono prelevare i messaggi.
Nella comunicazione indiretta due processi possono comunicare solo se hanno una mailbox in
comune.
A.C. Neve – Sistemi operativi 3.0
18
In questo schema le primitive send e receive hanno il seguente formato:
send(A, message);
invia il messaggio alla mailbox A;
receive(A, message);
riceve un messaggio dalla mailbox A.
Il canale di comunicazione ha le seguenti caratteristiche:
•
•
•
tra una coppia di processi si stabilisce un canale solo se entrambi i processi della coppia
condividono una mailbox;
un canale può essere associato a più di due processi;
tra ogni coppia di processi comunicanti possono esserci più canali diversi, ciascuno
corrispondente a una porta.
A.C. Neve – Sistemi operativi 3.0
19
Gestione dell’I/O
Il sistema operativo si deve occupare della gestione dell’I/O eseguendo i comandi destinati ai
dispositivi fisici, rimuovendo le condizioni di errore e risolvendo i problemi dovuti alla diversità
delle caratteristiche dei vari dispositivi. A tale scopo fornisce una interfaccia comune a tutti i
dispositivi, diposta tra i dispositivi fisici e il resto del sistema.
Con la sigla I/O si identificano delle unità fisiche che possono essere:
•
•
•
Dispositivi fisici di ingresso uscita (device)
Unità di controllo dei device in grado di gestire polling, interrupt o DMA con registri di I/O e
buffer (I/O controller)
Processori di I/O in grado di alleggerire il lavoro della CPU
I compiti che un sistema operativo deve eseguire nella gestione dell’I/O sono:
•
•
•
•
Stabilire a quale processo assegnare un device e per quanto tempo
Mantenere traccia dello stato del device
Allocare fisicamente il device al processo
Deallocare il device dal processo
Sono infine necessari dei componenti software detti device driver i quali sono specifici per ogni
device ed operano da interfaccia software tra il sistema operativo ed il device vero e proprio
gestendo le operazioni di I/O, le interruzioni e gli errori.
I device di I/O sono generalmente costituiti da parti elettromeccaniche con un funzionamento
spesso asincrono rispetto alla CPU rendendo così più complessa la loro gestione.
L’hardware di I/O è costituito dai dispositivi di I/O e dai relativi controllori.
I dispositivi possono differire sotto molti aspetti. Per quanto riguarda il trasferimento dei dati si
distinguono:
•
Trasferimenti a blocchi di dati o a stringhe di caratteri. I dispositivi a blocchi memorizzano le
informazioni in blocchi di lunghezza fissa, ognuno dei quali ha un proprio indirizzo ed ogni
blocco può essere indirizzato in maniera indipendente dagli altri. I dispositivi a caratteri
operano invece su flussi di caratteri.
•
Trasferimenti seriali o paralleli. I dispositivi seriali inviano o ricevono un bit alla volta, i
dispositivi paralleli trasferiscono più bit contemporaneamente.
•
Trasferimenti sincroni, che hanno tempi di risposta prevedibili, o asincroni, in cui i tempi di
risposta non sono prevedibili.
•
Dispositivi che consentono solo la lettura, solo la scrittura o entrambe le operazioni. Le
operazioni di lettura e scrittura vengono eseguite su un blocco o su un carattere a seconda del
tipo di dispositivo.
La modalità di accesso al dispositivo può essere sequenziale se il trasferimento avviene secondo un
ordine fisso, oppure casuale, se si accede direttamente ad una qualunque locazione di memoria.
Inoltre si distinguono i dispositivi condivisi, utilizzati da più utenti simultaneamente, da quelli
dedicati ai quali può accedere un utente alla volta.
La velocità di funzionamento è generalmente diversa per ogni dispositivo.
A.C. Neve – Sistemi operativi 3.0
20
Il software deve garantire buone prestazioni su sequenze di dati di diversi ordini di grandezza.
La gestione dell’I/O viene suddivisa in tre livelli:
• Il primo livello consiste nella definizione delle operazioni di sincronizzazione e trasferimento
delle informazioni tra device e processi. Le funzioni di questo livello rendono trasparenti
all’utente i meccanismi hardware e software usati.
Il trasferimento delle informazioni può avvenire per mezzo dei registri del processore o per
mezzo del gestore del DMA.
• Il secondo livello consente l’interfacciamento tra i processi ed i device in modo indipendente
dalle caratteristiche del device stesso. Tutto ciò avviene per mezzo dello specifico driver del
device il quale traduce i comandi provenienti dal software di sistema in comandi e segnali
interpretabili dall’hardware del device.
• Il terzo livello (detto Virtual File System) realizza una interfaccia uniforme per i processi utenti
che potranno così utilizzare un unico sistema di nomi per i device con uno schema di accesso
simile a quello dei file. Questo livello comprende anche le politiche mediante le quali il sistema
operativo ottimizza le prestazioni dell’utilizzo dei device.
Il sistema operativo permette l’aggiunta di nuovi dispositivi e la modifica delle caratteristiche di
quelli preesistenti. Poiché ogni dispositivo ha caratteristiche diverse, il sistema astrae dai dettagli
dei dispositivi individuando alcuni tipi generali per ognuno dei quali definisce una interfaccia. Le
effettive differenze tra i vari dispositivi saranno poi gestite dai driver specifici.
Inoltre la parte del sistema operativo che si occupa dell’I/O deve:
•
Garantire l’indipendenza del sistema dai dispositivi. Il codice del programma dal quale si
accede ad un dispositivo deve essere indipendente dal tipo di dispositivo, ovvero il codice per
essere eseguito su dispositivi diversi non deve essere modificato.
•
Gestire una denominazione uniforme dei dispositivi. Un dispositivo deve avere il solito nome
(file system o cdrom, o dischetti…). Tutti i file e i dispositivi sono indirizzati nello stesso
modo, utilizzando uno specifico path name.
•
Gestire il trattamento degli errori. La maggior parte degli errori sono temporanei e non si
ripresentano se l’operazioni viene ripetuta, quindi possono essere gestiti dal driver di
dispositivo, se non riesce il controllore, semplicemente rieseguendo il comando. In ogni caso è
preferibile che l’utente finale non si accorga degli errori, quindi devono essere gestiti al livello
più basso, più vicino all’hardware. Solo se i livelli più bassi non sono in grado di gestire
l’errore vengono avvertiti i livelli alti.
•
Gestire il trasferimento delle informazioni. Si distingue in trasferimento sincrono (a controllo di
programma) e trasferimento asincrono (a controllo di interruzioni o DMA). La maggior parte
dell’I/O è asincrono, ma i programmi utenti sono più semplici se le operazioni di I/O sono
sincrone. Il sistema deve mascherare all’utente il vero aspetto delle operazioni facendole
apparire sincrone.
Un’altra funzione del sottosistema di I/O è il buffering: esiste una area della memoria in cui
vengono memorizzati i dati trasferiti tra due dispositivi, o tra un’applicazione e un dispositivo.
Questa zona è chiamata buffer.
Senza la bufferizzazione delle informazioni, un processo viene bloccato in attesa che venga portata
a termine una operazione di I/O. Viene sbloccato ogni volta che arriva un dato, quindi torna nello
stato bloccato dopo il suo trasferimento.
La bufferizzazione viene fatta dal sistema operativo per conto del processo: i dati da trasferire al
processo vengono mantenuti in un buffer e solo quando l’operazione di I/O è terminata il processo
viene sbloccato e i dati trasferiti tutti in una volta dal buffer al processo stesso.
A.C. Neve – Sistemi operativi 3.0
21
La bufferizzazione può essere utile anche per gestire la differenza di velocità di trasferimento dei
dati tra processi (per esempio nel caso di processo produttore e processo consumatore).
Infine il sottosistema di I/O deve rendere possibile la condivisione delle risorse. Le risorse
condivisibili, per esempio il disco, non causano problemi. Esistono però risorse non condivisibili a
cui pervengono contemporaneamente più richieste. In questa situazione non è conveniente che la
gestione del dispositivo venga svolta dal processo, ma è il sistema operativo che, tramite lo
scheduling di I/O, stabilisce un ordine di esecuzione efficace delle richieste di I/O.
Per ogni dispositivo esiste una coda delle richieste (device queue); quando una applicazione esegue
una system call di I/O bloccante, la richiesta è aggiunta nella coda relativa al dispositivo
appropriato. Lo scheduler di I/O riorganizza la coda per migliorare le prestazioni, offrendo una
migliore efficienza globale del sistema e una diminuzione del tempo di attesa delle applicazioni.
Classificazione dei device
I device possono essere classificati per tipo di funzione, tempo di accesso, tipo di accesso, velocità
di trasferimento, codifica dei dati, tipologia dei comandi e condizione di errore.
In base alla funzione si ha:
• Human readable: usati per comunicare con gli utenti (terminali, tastiere, mouse, stampanti,
ecc.)
• Machine readable: usati per la comunicazione tra altri sistemi elettronici (sensori, attuatori,
ecc.)
• Communication: usati per comunicare con dispositivi remoti
• Memory device: usati per la memorizzazione delle informazioni
Il tempo di accesso è definito come l’intervallo di tempo che intercorre tra l’istante di richiesta
dell’informazione e l’istante in cui questa è disponibile.
In base al tipo di accesso si ha:
• Accesso sequenziale: sono device per i quali il tempo di accesso dipende dalla posizione del
dato e presenta quindi grandi oscillazioni del suo valore.
• Accesso casuale: sono device per i quali il tempo di accesso è costante.
• Accesso quasi casuale: sono device per i quali il tempo di accesso presenta una lieve
dipendenza dalla posizione del dato.
Un’altra classificazione riguarda l’impacchettamento dei dati trasferiti che possono essere a blocchi
(hard disk, CD ROM, stampanti) o a carattere (terminali, modem).
Per quel che riguarda le velocità di trasferimento, queste possono avere grandi differenze.
Struttura del software di I/O
Il software di I/O è organizzato in quattro livelli:
Livello utente
SW di I/O
Livello kernel
HW di I/O
A.C. Neve – Sistemi operativi 3.0
SW I/O LIVELLO UTENTE
⇓
SW I/O INDIPENDENTE DAI DISPOSITIVI
⇓
DRIVER DEI DISPOSITIVI
⇓
DRIVER DELLE INTERRUZIONI
HARDWARE
22
• La gestione delle interruzioni creata nell’hardware di I/O è completata dal driver delle
interruzioni. La gestione delle interruzioni non è semplice perché comporta l’esecuzione di una
serie di operazioni eseguite via software dal driver delle interruzioni, dopo che l’interruzione
hardware è stata avviata (nel livello hardware di I/O).
• Il driver di un dispositivo comanda l’operazione richiesta (read o write) poi si blocca perché
l’operazione di I/O richiede del tempo. Il driver viene bloccato da una interruzione provocata dal
driver delle interruzioni.
Indipendentemente dall’algoritmo di scheduling scelto si può verificare che, durante l’intervallo di
tempo in cui il driver è bloccato, un processo si sblocchi. Al termine dell’operazione di I/O il
processo che va in esecuzione è quello con priorità maggiore, mentre l’altro processo viene inserito
in una coda di attesa.
• Il livello del driver dei dispositivi gestisce la diversità delle caratteristiche dei dispositivi fisici e
rende indipendente il livello superiore dai particolari dispositivi. E’ costituito da un insieme di
driver, uno per ogni dispositivo presente o uno per una classe di dispositivi con caratteristiche
simili. In fase di installazione del dispositivo fisico il sistema operativo carica anche il
corrispondente driver. Se il sistema operativo implementasse un unico driver per rappresentare tutti
i dispositivi, tale driver dovrebbe raggruppare tutte le diverse caratteristiche che può assumere un
dispositivo ed avrebbe dimensioni enormi.
Per rendere trasparenti le caratteristiche dei dispositivi, il sistema operativo definisce una interfaccia
tra il livello del driver di dispositivo e il livello del software di I/O indipendente dai dispositivi. Tale
interfaccia permette ai diversi driver dei dispositivi di fornire al livello superiore un numero limitato
di funzioni, del tipo:
-
“write / read un carattere”, se il dispositivo trasferisce caratteri;
-
“write / read un blocco”, se il dispositivo trasferisce blocchi.
Queste procedure possono essere chiamate dal resto del sistema per interagire con il driver del
dispositivo.
• L’interfaccia definita a livello utente è generale, composta da funzioni limitate e tutte dello stesso
tipo per tutti i dispositivi: il livello del software di I/O indipendente dai dispositivi invia le funzioni
al livello del driver di dispositivo che ha il compito gestirle opportunamente.
Il driver accetta e traduce il comando di carattere generale in una sequenza di comandi relativi ad un
dispositivo, in un linguaggio di più basso livello, adatti per il controllore che il driver gestisce.
A questo punto il controllore insieme al driver cominciano ad eseguire l’operazione richiesta: in
alcuni casi questa fase risulta veloce, altre volte il completamento delle operazioni comporta una
perdita di tempo non trascurabile per il driver di dispositivo che è in attesa attiva. In questa
situazione il driver di dispositivo controlla lo stato del dispositivo:
•
Dispositivo occupato: inserisce la richiesta in attesa in una coda (coda delle richieste pendenti);
quando il dispositivo si libera, la scelta della richiesta da servire viene fatta secondo un
algoritmo di scheduling.
•
Dispositivo libero: il driver traduce l’istruzione e invia al dispositivo di I/O il comando, poi si
blocca in attesa dell’istruzione di ritorno eseguita dal dispositivo di I/O.
A.C. Neve – Sistemi operativi 3.0
23
Gestione dei dischi
Gli hard disk rappresentano la più importante tra le unità di I/O di un sistema di elaborazione in
primo luogo per la grande quantità di dati che possono conservare e per l’importanza che questi dati
possono avere. La parte principale è rappresentata dai file del sistema operativo che vengono
movimentati per la gestione di tutti i vari servizi offerti dal sistema operativo stesso.
Traccia
Testina
Braccio
Cilindro
Traccia
Disco
Gap
Motore
Settore
Gli hard disk sono costituiti da un insieme di dischi rotanti sullo stesso asse (7200 giri/min) e
ricoperti di materiale magnetico. Ogni faccia del disco contiene un insieme di tracce circolari
concentriche. Le tracce corrispondenti delle diverse facce costituiscono un cilindro. I dischi
vengono letti o scritti da un insieme di testine magnetiche che volano ad una microscopica distanza
dalla superfice del disco e sono movimentate da un braccio ad altissima precisione.
Le tracce sono suddivise in settori di capacità fissa (da 512 a 4096 byte), i settori consecutivi sono
separati da piccoli intervalli non magnetizzati detti gap.
I settori del disco rappresentano la minima unità di trasferimento hardware.
Via software è possibile definire una nuova unità minima di trasferimento multipla intera del settore
che viene denominata cluster.
Ogni settore è diviso in tre parti:
•
Preambolo: contiene le informazioni di controllo (numero della traccia, numero del settore…).
•
Dati: contiene i dati.
•
ECC (Error Correcting Code): contiene il codice a correzione di errore. Contiene sufficienti
informazioni affinché il controllore possa identificare i bit danneggiati e ricalcolare il loro
corretto valore.
Preambolo
A.C. Neve – Sistemi operativi 3.0
Dati
ECC
24
La formattazione del disco prevede cinque fasi:
•
Inizialmente il disco è vuoto e sarà fisicamente collegato e riconosciuto dal sistema.
•
Formattazione a basso livello: in questa fase vengono scritti i settori all’interno delle tracce del
disco. La formattazione a basso livello dà un formato al disco (da qui deriva il termine
“formattazione”) in modo che il controllore possa leggere o scrivere sul disco.
•
Creazioni delle partizioni del disco. Il sistema operativo suddivide il disco in gruppi di cilindri
(le partizioni) e tratta ogni gruppo come una unità a disco autonoma.
•
Formattazione ad alto livello: in questa fase vengono inseriti i file di sistema all’interno delle
partizioni del disco.
•
Installazione del sistema operativo.
La velocità operativa di un disco è caratterizzata dai seguenti parametri:
Seek time: tempo impiegato per individuare la traccia che contiene il blocco fisico; questo tempo è
proporzionale alla distanza che la testina deve percorre ad ogni spostamento.
Latency time: tempo che occorre per trovare il settore all’interno della traccia. In pratica è il tempo
di rotazione necessario per portare il settore sotto la testina.
Transfer time: tempo di lettura vero e proprio e di trasferimento del singolo blocco.
Il tempo necessario per una operazione di trasferimento è dato dalla somma dei tre tempi.
Indirizzamento:
Il file system vede i vari settori del disco allocati in uno spazio lineare di indirizzi LBA (Linear
Base Address), per contro l’accesso fisico al disco deve far uso di un indirizzamento
tridimensionale CHS (Cylinder – Head – Sector ). Il controller del disco dovrà quindi effettuare una
trasformazione degli indirizzi da coordinate LBA in coordinate CHS.
Interleaving:
Questo problema è dovuto al fatto che, durante il trasferimento, il disco continua a ruotare ed il
successivo settore passa sotto la testina mentre è in corso il trasferimento. In un giro è possibile
quindi leggere un solo settore. E’ pertanto opportuno numerare i settori in modo che il successivo
settore sia fisicamente distanziato di un adeguato numero di settori (ordine sequenziale a salti).
Algoritmi di scheduling
L’unica componente sulla quale si può agire per cercare di diminuire i tempi complessivi è il seek
time. Se arriva una richiesta quando il disco è libero, il sistema la serve normalmente. In questa
situazione non esiste alcun rimedio alla riduzione del tempo di esecuzione. Se il disco è occupato il
sistema può agire sul seek time sfruttando particolari algoritmi di scheduling.
Per ridurre i tempi, il sistema operativo sfrutta il principio di località per diminuire gli accessi al
disco. Quando riceve una richiesta il sistema legge non solo il settore che contiene la richiesta, ma
anche alcuni settori successivi. Il driver riceve le informazioni utili a soddisfare la richiesta, mentre
tutto ciò che è stato letto negli altri settori viene memorizzato in una cache. Quando arriva una
richiesta che coinvolge settori presenti nella cache il sistema non accede al disco, ma i dati da
passare al driver vengono prelevati dalla cache.
A.C. Neve – Sistemi operativi 3.0
25
Algoritmo FCFS
L’algoritmo FCFS (First-Come First-Served) organizza la lista delle richieste pendenti secondo
l’ordine di arrivo. L’inserimento e l’estrazione delle richieste risulta semplice, ma questo algoritmo
non permette di migliorare le prestazioni del sistema dal punto di vista del seek time.
Supponiamo di avere un disco con 200 tracce e di avere le seguenti richieste nella coda:
traccia richiesta
ordine di arrivo
98
1
183
2
37
3
122
4
14
5
124
6
65
7
67
8
Le richieste di lettura o di scrittura indicano la traccia su cui si trova il blocco che coinvolge
l’operazione.
Supponiamo inoltre che la testina sia posizionata inizialmente sulla traccia 53. Con l’algoritmo
FCFS il sistema serve le richieste nell’ordine di arrivo, quindi la testina si sposta dalla traccia 53
alla traccia 98, poi sulla traccia 183, poi sulla traccia 37 e così via.
Dopo aver servito l’ultima richiesta presente nella coda, la testina si è spostata complessivamente di
640 cilindri.
Questo algoritmo offre basse prestazioni perché il numero di spostamenti della testina è elevato: la
testina ogni volta si deve spostare casualmente da una parte all’altra del disco.
Algoritmo SSTF
L’algoritmo SSTF (Shortest Seek Time First) minimizza i tempi di ricerca. Il sistema serve le
richieste in base alla posizione della testina: seleziona dalla coda delle richieste pendenti quella più
vicina alla testina in quell’istante. L’inserimento delle richieste nella coda risulta ancora semplice,
mentre nell’estrazione il sistema deve scorrere tutta la lista per individuare la richiesta.
Utilizzando la situazione ipotizzata nel descrivere l’algoritmo FCFS, l’ordine di esecuzione delle
richieste con l’algoritmo SSTF diventa (si specificano le tracce su cui la testina si sposta):
65 → 67 → 37 → 14 → 98 → 122 → 124 → 183. Il risultato finale calcolato in numero di cilindri
visitati, è di 236 cilindri.
Questo algoritmo introduce starvation: se una richiesta è troppo lontana rispetto alle altre richieste
che continuano ad arrivare in coda, non verrà mai servita. Al contrario le richieste favorite sono
quelle che richiedono blocchi posizionati nelle tracce centrali al disco.
Algoritmo SCAN
L’algoritmo SCAN (noto come algoritmo dell’ascensore) effettua una scansione di tutte le tracce. Il
braccio del disco parte da un estremo, si sposta nella sola direzione possibile servendo tutte le
richieste che incontra sui cilindri, fino a raggiungere l’estremo opposto del disco. A questo punto
viene invertita la direzione (verso l’estremo da cui era partita) e riparte a servire le altre richieste.
Le testine attraversano continuamente il disco nelle due direzioni.
Questa soluzione elimina la starvation, perché sicuramente tutte le richieste vengono servite. Il
tempo massimo di attesa di una richiesta risulta, nella peggiore delle ipotesi, uguale al tempo
necessario alla testina ad attraversare per due volte il disco.
Se ripetiamo l’esempio precedente applicando questo algoritmo otteniamo 208 cilindri, risultato
migliore del caso precedente, ma in generale lo SCAN offre prestazioni peggiori del SSTF.
Al fine di migliorare la politica di questo algoritmo, è consigliabile far uso di due code relative alle
due direzioni di spostamento della testina.
A.C. Neve – Sistemi operativi 3.0
26
Algoritmo C-SCAN
Con l’algoritmo SCAN quando il braccio del disco torna indietro la zona vicino all’estremo è stata
visitata da poco, quindi probabilmente ci saranno poche richieste, mentre la maggior densità di
richieste sarà concentrata all’estremo opposto. Una variante di questo algoritmo (ottimizzazione)
prevede una scansione circolare del disco. Si parla quindi di C-SCAN: la testina si sposta da un
estremo all’altro come con lo SCAN, ma quando raggiunge un estremo riparte dall’inizio, senza
servire richieste nel viaggio di ritorno.
Il C-SCAN tratta il disco come una lista circolare.
Algoritmo LOOK
Esistono altri due algoritmi che derivano dall’algoritmo a scansione SCAN e C-SCAN che si
basano sull’idea che risulta inutile arrivare fino in fondo al disco se non ci sono più richieste oltre
una determinata traccia: con il LOOK (per lo SCAN) e il C-LOOK (per il C-SCAN) la testina si
sposta finché ci sono richieste da servire, quindi torna all’inizio.
RAID (Redundant Array of Indipendent Disks)
RAID è un insieme di dischi fisici visti dal sistema operativo come un solo disco logico con
migliori caratteristiche di affidabilità e prestazioni.
Il principio di funzionamento si basa sulle stripe (strisce): invece di registrare un file su un unico
disco, si suddivide il file in più parti e la registrazione viene fatta su più dischi in modo da
parallelizzare le operazioni ed incrementare la velocità.
L’affidabilità viene migliorata facendo uso di tecniche di ridondanza del tipo Hamming che
consentono di tollerare guasti su uno dei dischi dell’array.
Esistono diversi livelli di RAID da RAID 0 fino a RAID 6.
Tecniche di gestione
Un device di I/O può essere gestito nei seguenti modi:
•
•
•
Dedicato: Un device si dice dedicato quando viene assegnato ad uno specifico processo per
l’intera durata del processo stesso. In alcuni casi, questa tecnica può risultare inefficiente.
Condiviso: Un device si dice condiviso quando viene utilizzato da più processi durante la loro
evoluzione.
Virtuale: un device si dice virtuale quando viene trasformato da dedicato in condiviso per
mezzo di un opportuno software di controllo.
Spooling
Lo spooling (SPOOL Simultaneous Peripheral Operation On Line) è una tecnica per gestire
periferiche dedicate, come le stampanti, in un ambiente multiprogrammato.
L’I/O viene effettuato su una stampante virtuale implementata per mezzo di un file residente su
disco che viene gestito da un processo detto spooler che preleva l’output di stampa da questo file ed
effettua la stampa fisica.
Lo spooling utilizza il disco come un buffer di memoria acquisendo velocemente tutti i vari input e
provvedendo all’output secondo la velocità di acquisizione del device di stampa.
I programmi di spooling fanno parte del sistema operativo ed interagiscono con i gestori di
memoria, processori e device.
A.C. Neve – Sistemi operativi 3.0
27
Buffering
Il livello del software di I/O realizza il meccanismo di bufferizzazione, rende cioè disponibili dei
buffer per la memorizzazione temporanea delle informazioni per conto dei processi utente. Queste
aree di memoria sono utilizzate, come già specificato precedentemente, per evitare di bloccare il
processo ogni volta che arriva un dato.
Il sottosistema di I/O alloca e gestisce delle aree di memoria centrale destinate al transito di dati in
I/O in modo da rendere indipendenti le differenze di velocità tra il sistema e l’I/O.
L’utilizzo del buffer riduce l’overhed poiché consente ad un processo di leggere o scrivere su un
device senza doversi frequentemente bloccare.
I buffer possono essere implementati anche nell’interfaccia o nel controller di I/O.
Generalmente si usa un singolo buffer, ma risulta più efficiente l’utilizzo del doppio buffer (double
buffering): mentre il produttore riempie il primo, il consumatore svuota il secondo evitando così i
tempi di attesa.
Cache
Mediante questa tecnica si mantiene in memoria centrale una copia delle informazioni più
frequentemente usate che si trovano nella memoria secondaria (disco), in questo modo il processore
non preleva i dati dal disco che è lento ma li preleva dalla copia presente in memoria centrale con
maggiore velocità.
Nasce però il problema della scelta delle informazioni da tenere in cache e della sincronizzazione
con la relativa copia esistente sul disco. A tal proposito esistono degli algoritmi di gestione della
cache:
• FIFO (First In First Out): viene sostituito il blocco dati che da più tempo risiede in cache
• LRU (Last Recently Used): viene sostituito il blocco che da più tempo non è utilizzato
• LFU (Last Frequently Used): viene sostituito il blocco usato con minor frequenza.
Schedulatore dell’I/O
Lo schedulatore dell’I/O definisce le strategie di assegnazione dei device ai processi attraverso
l’utilizzo di vari tipi di algoritmi.
Una volta che lo schedulatore dell’I/O ha definito l’ordinamento delle richieste di I/O, il controllore
del traffico determina quali richieste possono essere soddisfatte in funzione dello stato del sistema
di I/O. Quando una di queste richieste viene attivata, generalmente viene condotta a termine.
Controllore del traffico di I/O
Il controllore del traffico mantiene traccia dello stato dei device, delle interfacce e dei canali e
verifica la possibilità di definizione di un percorso per asservire una richiesta di I/O.
Il controllore del traffico gestisce un archivio dati costituito da un blocco di controllo contenente:
Identificatore del device
Per ogni device
Per ogni interfaccia
Per ogni canale
Stato
Lista delle interfacce collegate
Lista dei processi in attesa del device
Identificatore dell’interfaccia
Stato
Lista dei device collegati
Lista dei canali collegati
Lista dei processi in attesa della unità di controllo
Identificatore del canale
Stato
Lista delle interfacce collegate
Lista dei processi i attesa dei canali
A.C. Neve – Sistemi operativi 3.0
28
Gestori dei device
Esiste un gestore (device driver) per ciascun device di I/O. Questo avvia e gestisce l’I/O fisico,
gestisce le interruzioni ed ottimizza le prestazioni con l’obiettivo di ridurre il tempo richiesto per le
operazioni di I/O.
I device driver sono dei programmi che fanno da interfaccia software tra il sistema operativo ed i
programmi applicativi. I device driver possono essere generali (device indipendent) o dedicati ad un
particole device. I device driver sono corredati di tutte le informazioni necessarie per l’utilizzo
hardware software della periferica in oggetto.
A.C. Neve – Sistemi operativi 3.0
29
Gestione della memoria centrale
La parte del sistema operativo che gestisce la memoria è il gestore di memoria il quale si occupa di
tutti i problemi legati all’assegnazione della memoria ai diversi processi che ne fanno richiesta per
portare a termine la loro esecuzione. Il gestore della memoria deve quindi tenere traccia di quali
parti di memoria sono in uso e quali sono libere e deve allocare o deallocare memoria ai processi in
modo opportuno. Infine, quando la memoria principale non è sufficiente a contenere tutti i processi,
il gestore deve provvedere a fornire una memoria di supporto tramite un meccanismo di
virtualizzazione della memoria.
Un programma per essere eseguito deve avere un’area di memoria a disposizione.
Il sistema operativo deve implementare dei meccanismi che allochino spazi di memoria ai processi
pronti ad andare in esecuzione e che revochino lo spazio ai processi che terminano.
Poiché la memoria principale non è sempre sufficiente a mantenere tutti i processi correntemente
attivi, il sistema operativo utilizza una memoria di supporto, generalmente i dischi, sulla quale
risiedono i processi in eccesso e che verranno introdotti successivamente in memoria principale.
Il sistema operativo deve realizzare una corrispondenza tra istruzioni del programma e indirizzi di
memoria. Tale associazione può essere stabilita in momenti diversi.
Se in fase di compilazione, il sistema operativo sa già dove risiederà il programma, esso genera un
indirizzamento assoluto del programma. Il codice così generato non è rilocabile, cioè se ad un dato
momento gli indirizzi delle locazioni di memoria che ospitano il programma dovessero cambiare,
sarebbe necessario ricompilare il programma.
Quando l’associazione è eseguita in fase di caricamento, il codice generato dal compilatore è invece
rilocabile. Quindi se l’indirizzo iniziale cambia si ricarica il programma in memoria senza dover
ricalcolare gli indirizzi.
Le opportune traduzioni sono effettuate dal loader.
Se l’associazione finale degli indirizzi è ritardata alla fase di esecuzione, il programma può essere
spostato da una zona di memoria all’altra durante l'esecuzione stessa. La maggior parte dei sistemi
operativi impiega questo metodo.
Gli indirizzi generati da un programma sono detti indirizzi logici, mentre gli indirizzi su cui opera
l’unità di memoria sono indirizzi fisici. L’insieme degli indirizzi logici generati da un programma
formano lo spazio degli indirizzi logici; l’insieme degli indirizzi fisici corrispondenti a tali indirizzi
logici formano lo spazio degli indirizzi fisici.
La definizione degli indirizzi virtuali sugli indirizzi fisici viene fatto dalla Memory Management
Unit (MMU). L’unità di MMU deve svolgere le seguenti funzioni:
•
•
•
Allocazione: cioè assegnazione della memoria alle entità che ne fanno richiesta
Deallocazione: rendere disponibile la memoria delle entità che non ne hanno più bisogno
Memorizzazione dello stato: tenere traccia sull’utilizzo delle varie parti di memoria.
L’unità di MMU svolge le funzioni elencate riferendosi a tre aspetti:
•
•
•
Hardware: che è la parte di circuiteria elettronica che gestisce tutti i chip di memoria fisica
Sistema Operativo: che gestisce, in modo trasparente, l’allocazione di memoria agli applicativi
Applicativi: i quali richiedono memoria al sistema operativo per mezzo funzioni specifiche del
linguaggio di programmazione utilizzato.
A.C. Neve – Sistemi operativi 3.0
30
Allocazione contigua
Partizioni statiche
Partizione singola
Nel caso di un sistema operativo monoutente monoprogrammato, una parte della memoria centrale
è allocata al sistema operativo mentre la restante parte è disponibile per il programma in esecuzione.
Non è necessario hardware di MMU se non per un registro di limite il cui compito è quello di
controllare che l’applicativo in esecuzione non acceda alla memoria allocata al sistema operativo.
L’algoritmo di allocazione ha il solo compito di asservire la richiesta di memoria dell’applicativo e
di controllare che le dimensioni del programma siano minori o uguali alla memoria disponibile.
Uno svantaggio di questa tecnica si nota quando il programma in esecuzione è molto piccolo
rispetto alle dimensioni della memoria centrale non sfruttando così le condizioni per l’esecuzione
contemporanea di un altro programma.
Partizioni multiple statiche
Nei sistemi multiprogrammati il sistema operativo consente a diversi processi di girare
contemporaneamente, quindi la memoria deve essere suddivisa in un certo numero di partizioni in
modo da soddisfare le esigenze dei processi.
Il modo più semplice prevede di dividere la memoria in partizioni fisse dette statiche, ognuna
dedicata ad un processo; terminato un processo la sua partizione torna ad essere libera per un altro
processo. Il grado di multiprogrammazione è limitato dal numero di partizioni. Più grande è la
memoria centrale e maggiore è il numero di partizioni o la loro dimensione.
Le partizioni sono definite dal sistema operativo ed ogni programma deve comunicare la quantità
massima di memoria richiesta.
Le partizioni statiche pongono il vincolo di dover caricare tutto il programma in memoria centrale
per cui se non esiste una partizione libera in grado di ospitare il programma, questo non sarà
eseguito. Un altro vincolo è relativo all’impossibilità di caricare il programma in più partizioni
libere non contigue.
Anche in questo caso si fa uso del registro limite il quale ha il compito di controllare che
l’applicativo resti sempre all’interno della partizione assegnata.
Spesso si utilizza una chiave di protezione definita per ogni partizione la quale consente l’accesso
alla partizione solo al programma al quale è stata assegnata quella specifica chiave.
I valori del registro limite o della chiave di protezione fanno parte dello stato del processo e sono
quindi memorizzati nel PCB (Process Control Block).
Uno degli svantaggi di questa tecnica è la frammentazione interna che si verifica quando un
processo utilizza solo parzialmente la partizione assegnata per cui lo spazio restante è formalmente
allocato ma di fatto non usato mentre potrebbe essere utilizzato da altri processi.
00000H
SISTEMA OPERATIVO
Partizione 1 AaaaaaaaaaProcesso 1aaaaaa aaa a
Aaaaaaaaaaaaaaaaaaaaaaaaa aaaaa
← frammentazione
Partizione 2 AaaaaaaaaaProcesso 2 aaaa aaaaaa
Aaaaaaaaaaaaaaaaaaaaaaaa aaaaaaa
Partizione 3 AaaaaaaaaaProcesso 3 aa aaaaaaaa
aaaaaaaaaaaaaaaaaaaaaa aaaaaaaaa
←frammentazione
←frammentazione
Partizione 4 aaaaaaaaaaaProcesso 4
A.C. Neve – Sistemi operativi 3.0
aaaaaaaa
FFFFFH
31
Supponendo di avere partizioni statiche ma di dimensioni differenti, sarà necessario selezionare la
partizione da assegnare al processo. A tal proposito esistono alcuni algoritmi:
FIRST FIT: effettua una ricerca tra le partizioni assegnando il processo alla prima partizione in
grado di contenerlo
BEST FIT: effettua una ricerca tra tutte le partizioni assegnando il processo alla partizione di
dimensioni più prossime alle dimensione del processo richiedente.
Per aumentare il grado di multiprogrammazione, il sistema operativo può, in base a dei criteri di
priorità, effettuare lo swapping del processo cioè salvare il processo su disco ed assegnare la
partizione ad un altro processo. Tutto ciò è possibile solo se il codice del processo è rilocabile cioè
risulta eseguibile indipendentemente dall’area di memoria centrale nella quale è caricato.
Quando viene caricato un processo, questo viene inserito in una coda di ingresso.
Esistono due diverse implementazioni:
- partizioni di memoria fisse con code di ingresso separate per ciascuna partizione
- partizioni di memoria fisse con coda di ingresso unica
Con il primo approccio, il processo caricato viene inserito nella coda d’ingresso della partizione più
piccola che lo possa contenere. Poiché le partizioni sono fisse tutto lo spazio di una partizione non
utilizzato dal processo è sprecato. Il caso peggiore si ha quando vi sono molti processi in attesa di
una partizione piccola mentre la coda di ingresso di una partizione grande è vuota: i processi piccoli
sono in attesa anche se gran parte della memoria risulta libera.
L’organizzazione dei processi in un’unica coda risulta più efficiente in termini di tempi di attesa:
appena una partizione di memoria è libera, viene occupata dal primo processo in coda che può
entrare nella partizione. Una ottimizzazione di questa implementazione consiste nel selezionare non
il primo processo in coda, ma cercare nella coda il processo più grande che può essere ospitato. In
questo modo si evita di sprecare partizioni grandi per far girare processi piccoli.
Partizioni dinamiche
Per eliminare il problema della frammentazione interna si ricorre alle partizioni dinamiche.
In questo caso, le dimensioni delle partizioni sono create dal sistema operativo in base alle richieste
dei processi che dovranno quindi essere in grado di comunicare le loro dimensioni.
Il sistema operativo dovrà quindi tenere traccia di due tabelle:
Tabella delle partizioni PDT (Partition Description Table) che registra l’indirizzo iniziale, le
dimensioni e lo stato di allocazione.
Tabella delle aree libere FDT (Free Description Table).
L’allocazione richiede l’analisi delle aree libere per scegliere quella da assegnare. Se quella scelta è
più grande del necessario sarà suddivisa in due parti, una da assegnare al processo ed una che
resterà libera per altri processi e che sarà registrata nella FDT.
Quando un processo viene deallocato sarà cancellato dalla PDT e registrato nella FDT.
Esistono tre diversi algoritmi di allocazione della memoria:
FIRST FIT: la tabella delle aree di memoria libere è ordinata per indirizzi crescenti delle aree stesse.
La ricerca inizia dagli indirizzi più bassi e prosegue fino a trovare la prima area libera in grado di
ospitare la partizione. La ricerca di aree libere adiacenti per l’unione è semplice.
Questa tecnica favorisce la formazione di grandi aree libere verso gli indirizzi alti
BEST FIT: in questo caso, la tabella delle aree di memoria libera è ordinata per dimensioni crescenti
delle aree libere. La prima area libera che risulta in grado di contenere la partizione è quella che
A.C. Neve – Sistemi operativi 3.0
32
garantisce il migliore adattamento e quindi il minimo spreco. Questa ricerca richiede mediamente
l’esplorazione di metà tabella.
Se la partizione allocata risulta leggermente più grande, la sua frammentazione genera partizioni
libere molto piccole e difficilmente utilizzabili (frammentazione esterna).
WORST FIT: come nel caso precedente, anche ora la tabella delle aree di memoria libera è ordinata
per dimensioni crescenti delle aree libere. In questo caso si alloca l’area più grande in grado di
contenere la partizione. Con questo criterio si tende ad evitare la creazione di partizioni libere
troppo piccole che sarebbero inutilizzabili.
I migliori algoritmi in termini di tempo e di utilizzo della memoria risultano i primi due.
Deframmentazione e riallocazione
La frammentazione della memoria può essere interna oppure esterna.
La frammentazione interna riguarda spazi di memoria allocati ai processi ma non utilizzati.
La frammentazione esterna è causata dal meccanismo di swapping: caricando e scaricando dalla
memoria centrale processi di dimensioni diverse, si creano molti buchi liberi. La soluzione a questo
problema è data dalla deframmentazione con la quale si riordina il contenuto della memoria libera
in un unico grande blocco. Questo procedimento richiede un tempo non trascurabile.
Dall’analisi condotta è evidente come si possano determinare delle aree libere non contigue la cui
somma potrebbe consentire l’allocazione di ulteriori processi qualora potessero essere rese
contigue. Si correda così l’MMU di una nuova funzione detta di deframmentazione che ha il
compito di ricompattare le aree occupate creando una e grande partizione libera.
La deframmentazione può essere effettuata:
• Periodicamente
• Quando si determina un elevato livello di frammentazione
• Quando non si trova una area libera.
E’ evidente che la deframmentazione potrà essere effettuata solo se il codice del programma risulta
rilocabile dinamicamente cioè modificabile all’atto dell’allocazione.
Allocazione non contigua
La non contiguità del processo di allocazione è relativa alla possibilità di poter allocare un processo
in aree di memoria separate e quindi fisicamente non adiacenti. E’ evidente che durante
l’esecuzione del processo sarà necessario che la traduzione degli indirizzi logici in quelli fisici tenga
conto di questa separazione dei vari pezzi di programma.
Paginazione
La paginazione consente di assegnare ad un processo, in modo trasparente all’utente, degli spazi di
memoria non contigui ovunque essi siano disponibili, risolvendo quindi il problema della
frammentazione esterna.
Lo spazio degli indirizzi logici di un processo viene diviso in parti uguali dette pagine, anche la
memoria viene suddivisa in parti delle stesse dimensioni dette blocchi (frames). Una pagina può
essere inserita in un blocco qualsiasi. Le pagine sono logicamente contigue, i blocchi non
necessariamente. Le dimensioni delle pagine sono scelte come potenza di 2 (1, 2, 4, 8 KBy).
A.C. Neve – Sistemi operativi 3.0
33
Il gestore della memoria ha il compito di:
•
•
•
•
•
Tenere traccia dello stato di allocazione delle pagine e dei blocchi.
Per ogni processo è necessaria una tabella delle corrispondenze tra pagine e blocchi.
E’ poi necessaria una tabella che specifichi, per ogni blocco, se è allocato o libero.
Una volta individuato il processo da allocare, sarà necessario verificare se sono disponibili
tanti blocchi per quante sono le pagine da allocare.
Al termine del processo sarà necessario deallocare i blocchi ed aggiornare le tabelle.
Il caricamento di un processo di dimensioni Dpro richiederà un numero N di pagine uguale a
INTERO(Dpro/Dpag) +1 con Dpag pari alla dimensione della pagina. Se Dpro non è multiplo
intero di Dpag si crea frammentazione interna. Per limitare la frammentazione dovremmo avere
pagine più piccole, ma ciò comporterebbe l’aumento della dimensione della tabella delle pagine
(che potrebbe occupare anche molta memoria) e dell’overhead associato.
L’organizzazione della tabella dei blocchi liberi è alquanto critica relativamente alla velocità di
ricerca dei blocchi liberi in quanto questi sono distribuiti in modo casuale.
Memoria virtuale
Il problema fondamentale della multiprogrammazione consiste nella limitata disponibilità della
memoria: la dimensione combinata di programmi, dati e stack può eccedere la dimensione della
memoria fisica per essi disponibile.
Si introduce quindi il meccanismo di virtualizzazione della memoria che permette di eseguire
processi non contenuti interamente nella memoria, basandosi sul fatto che l’intero programma non è
necessario caricarlo tutto in una volta.
Il sistema operativo mantiene quindi in memoria principale solo le parti del programma in uso e il
resto su una memoria ausiliaria. Spesso ci sono parti di programmi che non vengono quasi mai
eseguite (per esempio il codice relativo alla gestione degli errori) e lo spazio occupato può essere
utilizzato per mantenere contemporaneamente in memoria più programmi.
Con la virtualizzazione della memoria il sistema alloca ad un certo istante non tutto il programma,
ma solo la parte necessaria ad eseguire il set di istruzioni richieste.
Il termine virtuale deriva dal fatto che i processi vengono eseguiti come se avessero a loro
disposizione tutta la memoria centrale.
I vantaggi offerti dalla virtualizzazione sono molteplici:
•
•
•
la programmazione non è più vincolata dalla quantità di memoria fisica disponibile;
aumento del parallelismo, con una conseguente crescita della produttività della CPU;
diminuzione dei tempi di caricamento dei programmi in memoria.
Il sistema operativo ha il compito di caricare dal disco in memoria la pagine necessaria al processo
per proseguirne l’esecuzione. Nel caso in cui non ci fosse spazio libero in memoria si dovranno
eliminare delle pagine dalla memoria ricopiandole sul disco. Questa operazione è detta swapping.
Una limitata quantità di memoria determinerà una eccessiva movimentazione di pagine (thrashing)
che degraderà le prestazioni del sistema. Anche la ricerca della pagina sul disco determinerà una
riduzione della velocità di esecuzione.
A.C. Neve – Sistemi operativi 3.0
34
Paginazione a richiesta
Per realizzare la virtualizzazione è necessaria la presenza di una zona di memoria dove le pagine
risiedono in attesa di essere allocate per i diversi processi: esiste quindi un’area di swap, che
contiene le pagine temporaneamente non in memoria. L’area di swap è una zona della memoria di
massa cioè l’hard disk. Ovviamente serve un meccanismo (pager) che si occupi del passaggio delle
pagine dalla memoria principale alla memoria di massa e viceversa. In particolare il pager effettua
due operazioni:
- swap-out: salva il contenuto dello spazio virtuale dedicato al processo in memoria di massa;
- swap-in: trasferisce il contenuto dello spazio di memoria virtuale dalla memoria di massa
all’interno della zona di memoria fisica allocata al processo.
Se non è disponibile il meccanismo della rilocazione dinamica degli indirizzi il trasferimento dello
spazio virtuale del processo nell’operazione di swap-in deve avvenire nella stessa area di memoria
dove è stato allocato la prima volta. Se al contrario il codice è rilocabile, allora lo spazio virtuale
può essere trasferito in qualunque area di memoria libera.
m em o ria fisica
m em o ria di m assa
sw ap -o ut
sp azio
v irtuale del
p ro cesso
area di sw ap
swap-in
Mediante la virtualizzazione, il sistema operativo permette di eseguire un processo che non è stato
caricato per intero in memoria e le informazioni di cui ogni processo ha bisogno per essere eseguito
sono caricate in memoria, su domanda, quando necessario.
Si parla quindi di paginazione su richiesta: ad un certo istante solo alcune pagine si trovano in
memoria centrale; una pagina viene trasferita da memoria di massa a memoria centrale solo quando
è richiesta dal processo, ovvero quando un indirizzo tradotto da logico a fisico si riferisce una
pagina non presente in memoria centrale.
La presenza di una pagina in memoria è individuata da un bit di validità Bv associato ad ogni
entrata della tabella delle pagine. Durante la traduzione di un indirizzo, il sistema accede alla tabella
delle pagine e controlla tale bit:
Bit di validità = 1, la pagina è presente in memoria centrale. Il sistema procede normalmente.
Bit di validità = 0, la pagina è in memoria di massa.
Se la pagina non è presente in memoria centrale (Bv=0) il sistema genera una interruzione di pagefault. Quindi il gestore delle interruzioni controlla la causa del page-fault e procede di conseguenza:
•
Se il page-fault è stato generato da un errore di accesso, segnalato dal bit di accesso presente
nella tabella delle pagine, il programma abortisce.
•
Se il page-fault è stato generato dal bit di validità, si accede alla tabella che contiene il
corrispondente indirizzo della pagina nell’area di swap. Individuata la pagina, il sistema cerca
un frame libero in memoria centrale e carica la pagina nello spazio libero (operazione di swapin). Quindi aggiorna la tabella delle pagine e riavvia il processo che era stato bloccato
dall’operazione che aveva provocato page-fault.
A.C. Neve – Sistemi operativi 3.0
35
Se occorre fare una operazione di swap-in ma non esiste un blocco libero in memoria centrale il
sistema attua il meccanismo di rimpiazzamento: il sistema operativo seleziona, secondo opportuni
criteri, una pagina presente in memoria centrale e con l’operazione di swap-out la copia in memoria
di massa, quindi effettua lo swap-in richiesto. Se la pagina selezionata non è stata mai modificata
dal momento in cui è stata caricata in memoria centrale (individuato dal dirty bit contenuto nella
tabella delle pagine), il sistema effettua solo l’operazione di swap-in, e non si ha quindi il
trasferimento da memoria centrale a memoria di massa.
Il meccanismo di paginazione su domanda permette di gestire più utenti contemporaneamente e
impiega per ogni processo meno memoria fisica.
La dimensione della pagina è un parametro che può influire sull’efficienza del sistema.
Pagine molto piccole riducono la frammentazione interna e migliorano l’utilizzo della memoria per
contro però fanno aumentare le dimensioni delle tabelle di gestione e la frequenza di
movimentazione. Il contrario accade per pagine molto grandi.
Strategie di allocazione
Le strategie di allocazione servono per decidere quanta memoria fisica è opportuno allocare ad ogni
processo.
Assegnare un elevato numero di pagine fisiche ad un processo riduce la frequenza di page fault e il
tempo di elaborazione delle richieste di pagina (turnaround). Si ha però una riduzione dei processi
attivabili e quindi del grado di multiprogrammazione.
L’assegnazione di un limitato numero di pagine incrementa la frequenza di page-fault e del
turnaround.
Si può verificare che, la relazione esistente tra la frequenza di page fault ed il numero di pagine
allocate non è lineare. Per bassi valori del numero di pagine si ha un notevole incremento del page
fault mentre per valori molto alti del numero di pagine non si ha un apprezzabile riduzione del page
fault. Il valore ottimale si trova tra questi due estremi.
Thrashing
Se il numero di pagine allocate ad ogni processo è tale da far aumentare notevolmente la frequenza
di page fault il sistema assume un comportamento detto di thrashing, il sistema cioè trascorre gran
parte del suo tempo in operazioni di swapping di pagine con un impegno di CPU apparentemente
basso. Lo schedulatore, notando il ridotto impegno della CPU, tenterà di aumentare il numero dei
processi attivi peggiorando ulteriormente le prestazioni.
A tal proposito è possibile definire una soglia inferiore e superiore della frequenza di page fault per
ciascun processo. Quando un processo supera la soglia superiore si allocano più blocchi quando
invece raggiunge la soglia inferiore si rallenta l’allocazione di ulteriori blocchi.
Working set
L’utilizzo del working set si basa sulle seguenti considerazioni:
•
•
•
Durante un qualsiasi intervallo di tempo, un processo in esecuzione favorisce un certo
sottoinsieme delle proprie pagine (località spaziale)
La sequenza di accesso alla memoria da parte di un processo in esecuzione evidenzia una alta
correlazione tra il passato immediato e l’immediato futuro (località temporale)
La frequenza con la quale una certa pagina è usata è una funzione che varia lentamente nel
tempo.
A.C. Neve – Sistemi operativi 3.0
36
Il working set di un processo è l’insieme delle pagine che definiscono la località delle pagine in un
certo istante cioè l’insieme delle pagine nell’intorno delle quali il processo si evolve.
Quando un processo si sposta da una località ad un’altra cambia anche l’insieme delle pagine che
costituiscono il suo working set in quell’intervallo di tempo.
La scelta dell’intervallo di tempo di osservazione del processo è molto importante:
se troppo piccolo, il working set potrebbe non contenere la giusta località, se invece è troppo grande
potrebbe estendersi inutilmente oltre la sua località.
Dal principio del working set consegue che:
• un processo può andare in esecuzione solo se il suo working set è in memoria
• una pagina non potrà essere rimossa se appartiene al working set di un processo in esecuzione.
Strategie di sostituzione
Quando si verifica un page-fault il processo non può più continuare la sua esecuzione ed il sistema
deve caricare in memoria centrale la pagina che contiene l’istruzione che ha provocato il fault. Se
non c’è spazio in memoria si dovrà operare un rimpiazzamento di pagina, cioè si sostituisce una
pagina con una già presente in memoria centrale selezionandola secondo il criterio adottato dal
sistema operativo. La gestione dei page-fault comporta una perdita di tempo notevole: il sistema
deve minimizzare la frequenza di page-fault agendo sul numero di blocchi a disposizione per ogni
processo e sugli algoritmi di sostituzione delle pagine. Una volta scelta la pagina da deallocare,
potrà essere necessario salvarla prima su disco nel caso in cui fosse stata modificata, in caso
contrario potrà essere tranquillamente distrutta, in molti casi questa soluzione può far risparmiare il
tempo di trasferimento.
Questa tecnica viene implementata per mezzo della tabella che descrive lo stato dei blocchi: si fa
uso di un bit (dirty bit) che viene posto a zero all’atto del caricamento della pagina e posto ad uno
ad ogni scrittura indicando così la variazione del contenuto e quindi la necessità di salvataggio.
Il sistema operativo deve quindi far uso algoritmi che raggiungono questo scopo e che nello stesso
tempo risultino facili da implementare e da gestire.
Gli algoritmi di sostituzione possono essere locali o globali:
La sostituzione è locale se la pagina eliminata appartiene al processo che ha generato il page- fault.
E’ invece di tipo globale se la pagine scelta può appartenere a qualsiasi processo.
Algoritmo ottimale
L’algoritmo ottimale (ma impossibile da implementare) sostituisce la pagina che non sarà più
utilizzata o non lo sarà per molto tempo. Secondo questo criterio la frequenza del page-fault è
minima. Inoltre si può dimostrare che non soffre dell’anomalia di Belady più avanti descritta.
Il problema fondamentale di questo algoritmo sta nel fatto che è irrealizzabile, infatti non è
possibile prevedere quando ciascuna pagina sarà riferita. E’ quindi un algoritmo ideale, utile solo
come algoritmo di riferimento e può essere utilizzato insieme ad un algoritmo realizzabile per
calcolarne le prestazioni.
Algoritmo FIFO
L’algoritmo FIFO (First In First Out) segue la politica secondo la quale si seleziona la pagina che
da più tempo si trova in memoria. Il sistema ha due modi di riferire le pagine:
•
mantenere un timer, inizializzato all’istante in cui la pagina viene caricata in memoria centrale;
ogni volta che si verifica un page-fault il sistema scorre tutte le pagine e seleziona quella più
vecchia.
A.C. Neve – Sistemi operativi 3.0
37
•
organizzare le pagine presenti in memoria in una lista concatenata in cui le pagine sono caricate
in ordine di arrivo in memoria centrale. Quando si ha il fault, la pagina in testa alla lista viene
eliminata e la nuova pagina viene inserita in coda.
Non si può però affermare con certezza che una pagine che risieda da molto tempo in memoria non
sia comunque tra quelle di uso più frequente.
Si consideri come esempio un processo che dispone in memoria di solo tre blocchi fisici e che nella
sua esecuzione acceda alle pagine (1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5)
il flusso di page-fault è il seguente:
ST E P
A Z IO N E
P A G IN E IN
M E M O R IA
1° 2° 3°
PA GE
FA U L T
p asso 0 : p agin e fisich e v uo te.
0
p asso 1 : il sistem a carica le p rim e tre p agin e rich ieste.
1
2
3
3
p asso 2 : p age fault sulla p agin a 4 . Il sistem a so stituisce la p rim a p agin a.
4
2
3
1
p asso 3 : p age fault sulla p agin a 1 . Il sistem a rim p iazza la seco n da p agin a.
4
1
3
1
p asso 4 : p age fault sulla p agin a 2 . Il sistem a rim p iazza la terza p agin a.
4
1
2
1
p asso 5 : p age fault sulla p agin a 5 . Il sistem a rim p iazza la p rim a p agin a.
5
1
2
1
p asso 6 : rich iesta la p agin a 1 , già p resen te in m em o ria cen trale.
5
1
2
0
p asso 7 : rich iesta la p agin a 2 , già p resen te in m em o ria cen trale.
5
1
2
0
p asso 8 : p age fault sulla p agin a 3 . Il sistem a rim p iazza la seco n da p agin a.
5
3
2
1
p asso 9 : p age fault sulla p agin a 4 . Il sistem a rim p iazza la terza p agin a.
5
3
4
1
p asso 1 0 : rich iesta la p agin a 5 , già p resen te in m em o ria cen trale.
5 3 4
N u m ero to ta le d i p a g e fa u lt
0
9
Disponendo invece di quattro blocchi fisici si ha il seguente flusso di page-fault:
ST E P
A Z IO N E
P A G IN E IN
M E M O R IA
1° 2° 3° 4°
p asso 0 : p agin e fisich e v uo te.
PA GE
FA U L T
0
p asso 1 : il sistem a carica le p rim e quattro p agin e rich ieste.
1
2
3
4
4
p asso 2 : rich iesta la p agin a 1 , già p resen te in m em o ria cen trale.
1
2
3
4
0
p asso 3 : rich iesta la p agin a 2 , già p resen te in m em o ria cen trale.
1
2
3
4
0
p asso 4 : p age fault sulla p agin a 5 . Il sistem a rim p iazza la p rim a p agin a.
5
2
3
4
1
p asso 5 : p age fault sulla p agin a 1 . Il sistem a rim p iazza la seco n da p agin a.
5
1
3
4
1
p asso 6 : p age fault sulla p agin a 2 . Il sistem a rim p iazza la terza p agin a.
5
1
2
4
1
p asso 7 : p age fault sulla p agin a 3 . Il sistem a rim p iazza la terza p agin a.
5
1
2
3
1
p asso 8 : p age fault sulla p agin a 4 . Il sistem a rim p iazza la p rim a p agin a.
4
1
2
3
1
p asso 9 : p age fault sulla p agin a 5 . Il sistem a rim p iazza la seco n da p agin a.
4 5 2 3
N u m ero to ta le d i p a g e fa u lt
1
10
Come si può notare, la situazione peggiora. Il fenomeno descritto è noto come anomalia di Belady
secondo il quale il numero di page-fault aumenta all’aumentare del numero di pagine allocate.
Si può quindi affermare che questo algoritmo non è molto efficiente.
A.C. Neve – Sistemi operativi 3.0
38
Algoritmo LRU (Least Recently Used)
L’algoritmo LRU si basa sull’idea che le pagine che sono state frequentemente usate nelle ultime
istruzioni lo saranno anche nelle prossime istruzioni, mentre quelle non utilizzate da lungo tempo
non lo saranno ancora per molto tempo.
Con l’algoritmo LRU il sistema interpreta il futuro sulla base delle informazioni passate e seleziona
la pagina non utilizzata da più lungo tempo, supponendo che non sia più utile. L’algoritmo quindi
ragiona in modo contrario all’algoritmo ottimale in cui il sistema prevede il futuro.
Il LRU viene implementato fornendo una ulteriore informazione in ogni entrata della tabella delle
pagine: si introduce un campo che indica il tempo ogni volta che una pagina viene riferita. La CPU
deve mantenere aggiornato un contatore, incrementato automaticamente ad ogni istruzione, che
tiene traccia del tempo che passa ed è utilizzato per aggiornare il campo nella tabella delle pagine. Il
sistema seleziona dalla tabella delle pagine la pagina con il campo minore.
Questo algoritmo non è molto efficace in quanto:
•
devono essere aggiunte informazioni nella tabella delle pagine, con il conseguente aumento
della dimensione della tabella stessa;
• per selezionare la pagina da sostituire il sistema deve fare una scansione di tutta la lista, che
comporta una perdita di tempo;
• deve essere prevista una soluzione anche per la situazione in cui il campo è inizializzato a 0;
• occorre stabilire un numero di bit opportuno per il campo presente nella tabella: deve essere
grande abbastanza da contenere il valore del contatore, ma non troppo grande per evitare di
aumentare notevolmente la dimensione della tabella delle pagine.
Questo algoritmo presenta però un comportamento migliore del precedente.
Algoritmo NRU (Not Recently Used)
Per implementare l’algoritmo NRU il sistema operativo si avvale di due informazioni riguardanti le
pagine caricate in memoria centrale.
Nella tabella delle pagine vengono memorizzati due bit di stato associati a ciascuna pagina:
•
il bit RB (reference bit) posto a 1 ogni volta che la pagina viene riferita (read);
•
il bit DB (dirty bit) che quando vale 1 indica che la pagina, dal momento in cui è stata caricata
in memoria centrale, è stata modificata (write).
Questi due bit devono essere aggiornati ogni volta che la pagina viene riferita, per questo motivo
vengono assegnati dall’hardware. Una volta che un bit è stato impostato a 1, sarà il sistema
operativo a riportarlo al valore 0 via software al momento opportuno.
L’algoritmo NRU utilizza il bit RB e il bit DB per selezionare la pagina da rimpiazzare.
A seconda del valore dei due bit, si identificano delle classi di pagine diverse:
RB
0
0
1
1
DB
0
1
0
1
CLASSE
pagine da molto tempo non riferite e non modificate
pagine da molto tempo non riferite ma modificate
pagine da poco tempo riferite e non modificate
pagine da poco tempo riferite e modificate
Tra tutte le pagine appartenenti alle diverse classi, quelle ad avere la maggior probabilità di essere
rimpiazzate sono le pagine della classe avente RB=0 e DB=0. Quelle invece meno convenienti da
essere sostituite sono le pagine appartenenti alla classe con RB=1 e DB=1. Tra le due classi
intermedie, il sistema privilegia nella scelta della sostituzione le pagine con RB=0 e DB=1 anche se
A.C. Neve – Sistemi operativi 3.0
39
comporta una maggior perdita di tempo (comporta anche l’operazione di swap-out, cosa che non
accade se è selezionata una pagina con RB=1 e DB=0).
Algoritmo LFU (Less Frequently Used) e MFU (Most Frequently Used)
Le tecniche viste fino ad ora si basano sull’istante di utilizzo delle pagine.
Esistono poi altri algoritmi, poco utilizzati, che si basano su altri criteri ed in particolare sulla
frequenza di utilizzo: l’algoritmo LFU e l’algoritmo MFU. Entrambi mantengono un contatore del
numero di riferimenti che sono stati fatti a ciascuna pagina.
Con l’algoritmo LFU il sistema controlla la frequenza di utilizzo di tutte le pagine nel passato. Se
una pagina ha bassa frequenza si presuppone che la pagina non sia utile, per questo viene
selezionata per il rimpiazzamento.
Al contrario, con l’algoritmo MFU il sistema seleziona per la sostituzione la pagina più
frequentemente utilizzata. Questo criterio si basa sull’idea che se una pagina è stata poco utilizzata
significa che è stata caricata da poco in memoria centrale, quindi non è conveniente toglierla perché
probabilmente fa parte del set di lavoro corrente.
Modalità di assegnazione dei blocchi
Il sistema può implementare due diversi schemi principali di allocazione dei blocchi della memoria
ai processi che ne fanno richiesta:
•
statica. Il numero dei blocchi allocati ad un processo rimane invariato per tutta la sua
esecuzione.
•
dinamica. Il numero dei blocchi allocati ad un processo può variare durante l’esecuzione.
Dato uno spazio di memoria totale composto da m blocchi ed n processi in esecuzione, in entrambe
le implementazioni viste sopra esistono due metodi di allocazione dei blocchi: allocazione uniforme
o allocazione proporzionale.
L’allocazione uniforme è la soluzione più semplice: sistema alloca a tutti i processi lo stesso
numero di blocchi, pari ad m/n. Questo metodo causa iniquità tra i processi perché svantaggia i
processi di dimensioni elevate che avrebbero bisogno di un numero maggiore di blocchi rispetto a
quelli allocati.
Con l’allocazione proporzionale il sistema alloca i blocchi della memoria in base alle dimensioni
dei processi che ne fanno richiesta. E’ un criterio più equo rispetto all’allocazione uniforme poiché
tiene conto delle diverse caratteristiche dei processi. In particolare il numero di blocchi allocati ad
ogni processo viene calcolato in questo modo: dati Pi processi, ognuno con la rispettiva richiesta di
memoria Si, si definisce la memoria totale disponibile:
n
S tot = ∑ S i
i =1
quindi il numero di blocchi allocati ad ogni processo è definito dalla seguente relazione:
mi =
Si
⋅ mtot
S tot
A.C. Neve – Sistemi operativi 3.0
40
Quando si verifica un page-fault ma non ci sono blocchi liberi in memoria, il sistema avvia il
meccanismo di rimpiazzamento che varia a seconda del tipo di allocazione dei blocchi:
•
rimpiazzamento locale: se l’allocazione è statica il sistema sceglierà la pagina da sostituire tra
quelle appartenenti al processo stesso che ha generato il fault, perché il numero dei blocchi
allocati deve rimanere uguale durante tutta l’esecuzione;
•
rimpiazzamento globale: se l’allocazione è dinamica la pagina rimpiazzata potrà appartenere a
qualsiasi processo in esecuzione. Il sistema prima di effettuare una sostituzione deve
controllare che i blocchi allocati a quel processo non scendano al di sotto del numero minimo.
La sostituzione globale diminuisce il tasso di page-fault complessivo del sistema.
Per il rimpiazzamento dei blocchi il sistema può basarsi anche su altri criteri, per esempio può tener
conto della priorità dei processi: i processi con priorità minore non possono togliere blocchi ai
processi con priorità maggiore.
A.C. Neve – Sistemi operativi 3.0
41
Gestione del File System
Introduzione
La parte del sistema operativo che interagisce direttamente con l’utente è il File System.
Questo permette all’utente di gestire e memorizzare i dati tramite l’utilizzo dei file e delle directory,
che sono parte integrante del file system.
Tutte le applicazioni effettuano la memorizzazione ed il recupero delle informazioni.
Una possibile soluzione a questo problema prevede che un processo in esecuzione memorizzi una
quantità limitata di informazioni nel suo spazio di indirizzamento.
Ovviamente questa implementazione è in conflitto con le richieste delle applicazioni:
•
i processi devono avere la possibilità di memorizzare una grande quantità di informazioni,
mentre questa soluzione offre una capacità di memorizzazione limitata dalla dimensione dello
spazio di indirizzamento virtuale;
•
le informazioni memorizzate servono anche quando il processo che le sta utilizzando termina,
mentre se vengono memorizzate nel suo spazio di indirizzamento del processo vengono perse;
•
i processi devono poter condividere le informazioni, ciò non è possibile con questa soluzione.
L’alternativa è quella di memorizzare le informazioni sui dischi o altri supporti esterni, in entità
chiamate file. Questa implementazione risolve tutti i problemi descritti in precedenza:
le informazioni nei file sono permanenti e un file viene eliminato solo su esplicita richiesta
dell’utente. I file sono gestiti dal sistema operativo che si occupa della loro implementazione e di
tutte le problematiche relative a tali entità.
La parte del Sistema Operativo che si occupa dei file è il File System ed è composto dall’insieme
dei file e dalle informazioni necessarie alla gestione stessa dei file.
Il File
Il sistema operativo definisce una unità di informazione logica (il file) e la rende disponibile
all’utente che può far riferimento al file tramite il suo nome ed i relativi attributi.
L’utente interagisce direttamente con il File System e non si occupa di come sono memorizzate le
informazioni all’interno del file.
Il file può essere rappresentato:
•
•
•
senza struttura: il file è visto come una sequenza non strutturata di byte;
con una struttura minima: il file è composto da una sequenza di record di lunghezza fissa,
ognuno con una propria struttura interna;
con una struttura più complessa: il file è formato da un albero di record e campi, non tutti
necessariamente della stessa dimensione.
Anche i dati che contiene possono essere diversi: un file può contenere informazioni numeriche,
alfabetiche, alfanumeriche o in formato binario.
Gli attributi che il sistema operativo associa a dei file, organizzati nella struttura di directory, sono
diversi e variano a seconda del sistema:
•
Nome: attributo mediante il quale l’utente identifica il file. Il sistema operativo lo identifica
invece tramite un ID, ovvero una sequenza di numeri.
A.C. Neve – Sistemi operativi 3.0
42
•
•
•
•
•
•
Tipo: attributo utilizzato dal sistema operativo il quale specifica al sistema stesso l’applicazione
da utilizzare per operare su quel file. Alcuni sistemi operativi impongono un tipo predefinito. Il
tipo è indicato come estensione del nome del file.
Locazione: specifica la posizione del file all’interno del disco o in qualsiasi altro supporto di
memorizzazione.
Dimensione: specifica la dimensione del file.
Protezione: specifica chi può accedere al file e quali operazioni può eseguire sul file.
Proprietario: proprietario corrente del file.
Tempo: specifica la data di creazione del file.
Il compito di un file system è quello di sollevare l’utente dalla necessità di dover compiere le
operazioni di movimentazione dei dati dalla memoria principale alla memoria secondaria che
generalmente consistono nel:
• Traduzione delle richieste di accesso dallo spazio logico degli indirizzi a quello fisico
• Gestione della memoria secondaria tenendo traccia delle allocazioni e disallocazioni
• Trasferimento degli elementi che costituiscono il file
• Gestione dei meccanismi di protezione e condivisione dei file.
Le operazioni possibili sui file vengono eseguite tramite system call e variano in funzione della
struttura del sistema operativo utilizzato. Il file system mantiene un puntatore al file: quando si
effettua una scrittura tale puntatore è posizionato alla fine del file, in caso di lettura punta la
locazione di memoria specificata. Oltre alla lettura e scrittura del file, le operazioni più utilizzate
sono le seguenti:
•
•
•
•
•
•
•
•
•
Create: alloca un elemento nella struttura di directory. Il sistema individua lo spazio opportuno
sul disco e assegna agli attributi del file gli opportuni valori; in particolare memorizza la
posizione di partenza del file sul disco nell’attributo locazione.
Delete: cancella il file. Il sistema operativo individua la locazione del file sul disco e dealloca
lo spazio.
Open: apre il file. Restituisce un identificatore utilizzato per effettuare le operazioni successive
sul file.
Close: chiude il file.
Write: scrive dei dati nel file. Ha come parametri il nome del file e le informazioni da scrivere.
Se la posizione corrente è alla fine del file, la dimensione del file viene estesa. Se la scrittura
avviene nel mezzo del file, i dati esistenti vengono sovrascritti dai nuovi.
Read: legge i dati dal file. Ha come parametri il nome del file e la locazione di memoria da
leggere.
Seek: posiziona il puntatore alla posizione corrente.
Truncate: elimina una quantità di dati dal file.
Append: Aggiungere nuovi dati in coda al file.
Directory
Il File System tiene traccia dei file per mezzo delle directory.
Una directory è modellizzabile come un contenitore di file che viene gestito per mezzo dei
descrittori di file.
In generale le operazioni che si possono eseguire su una directory, oltre alla creazione e alla
cancellazione della struttura, riguardano la creazione o la cancellazione di un file, la ricerca di un
file tramite lo scorrimento della directory e la possibilità di elencare tutti i file contenuti in una
directory, con le relative informazioni.
A.C. Neve – Sistemi operativi 3.0
43
Le directory sono utilizzate per organizzare i file all’interno del File System ed esistono diversi
modelli implementativi.
Il sistema operativo deve prima di tutto tenere conto delle richieste dell’utente, ovvero:
•
•
•
Efficienza: le operazioni devono essere veloci.
Nominazione: attributo riferito ai nomi dei file. All’interno di una directory i nomi dei file
devono essere unici. Il sistema operativo deve controllare che non ci siano conflitti.
Raggruppamento logico dei file: l’utente dispone di diversi file che devono essere raggruppati
logicamente in classi di categorie diverse.
DIRECTORY FLAT
La più semplice organizzazione logica delle directory è rappresentata dalla struttura a singolo
livello, in cui tutti i file sono contenuti in una directory comune. Il File System è organizzato su un
unico livello, composto da una unica directory i cui elementi sono i descrittori dei file.
descrittore
file1
file1
directory
descrittore
...
file2
file2
...
descrittore
fileN
fileN
Per creare un file il sistema deve prima di tutto controllare se esiste già un file con il nome
specificato dall’utente, scorrendo tutta la struttura di directory. Se non esiste, compie una sequenza
di operazioni:
•
•
•
alloca lo spazio sul disco per il file;
introduce un nuovo elemento nella struttura di directory, inizializzando gli attributi secondo le
specifiche dell’utente;
alloca il puntatore del file allo spazio su disco.
Per la cancellazione di un file, il sistema esegue analoghe operazioni: il file system cerca nella
struttura di directory l’elemento con il nome specificato dall’utente, libera lo spazio sul disco e
cancella il descrittore del file.
L’organizzazione a singolo livello non risponde a nessuna delle esigenze richieste dall’utente in
quanto scomoda e spesso inadeguata.
DIRECTORY a Due LIVELLI
Una alternativa all’organizzazione a singolo livello è fornita dalla struttura delle directory su due
livelli, comunemente chiamata struttura a doppio livello. In questa struttura compare una directory
principale costituita da tanti elementi quanti sono gli utenti; ogni elemento della directory
principale contiene un puntatore ad una directory secondaria (directory utente o sottodirectory)
organizzata come nella struttura a singolo livello.
Rispetto alla struttura a singolo livello, con questa organizzazione la gestione dei nomi dei file
risulta semplificata per l’utente e per il sistema, poiché ogni directory contiene un minor numero di
file ed è possibile far uso di nomi di file uguali per sottodirectory differenti.
A.C. Neve – Sistemi operativi 3.0
44
All’interno della directory secondaria si ripetono i problemi visti nella struttura a singolo livello: il
sistema non fornisce un supporto per i file del singolo utente, ma li raggruppa in una unica
directory.
Per fare riferimento ad un file il sistema deve fare un doppio scorrimento: il primo per trovare
l’utente a cui appartiene il file, il secondo per trovare il file. In generale il primo scorrimento può
essere evitato, caricando in memoria la directory dell’utente che sta effettuando un accesso al
sistema. Le volte successive il sistema non accede più al disco e le operazioni risulteranno più
veloci.
Utente1
Utente2
UtenteN
...
descritt.
file1
descritt.
file2
descritt.
file5
descritt.
file7
descritt.
file8
file1
file2
file5
file7
file8
...
directory principale
descritt.
file3
descritt.
file4
sottodirectory
file3
file4
file
DIRECTORY GERARCHICHE
Generalizzando l’organizzazione a doppio livello delle directory, si ottiene la struttura ad n livelli
nelle quale il primo livello è sempre la radice, il livello successivo è composto dagli utenti ed infine
per ogni utente possono essere previste più directory per formare un albero.
Ogni directory può raggruppare sia file che sottodirectory.
/
Utente1
PROG
C++
Utente2
VARIE
JAVA
f2.cpp
directory radice
...
UtenteN
PROG
HTML
f7.htm
directory utente
GAMES
COREL
DOC
sottodirectory utente
WORD
EXEL
f3.doc
f4.xls
sottodirectory utente
file utente
DIRECTORY a GRAFO
Abbiamo visto che l’organizzazione migliore per le directory è quella ad albero generico, in cui i
descrittori dei file si trovano all’interno delle directory proprietarie. Con questa organizzazione di
directory non è consentito che un file si trovi contemporaneamente sotto due o più directory diverse.
A.C. Neve – Sistemi operativi 3.0
45
Il sistema deve quindi implementare un meccanismo che risolva il problema della condivisione dei
dati tra più utenti. Questo problema riguarda sia i singoli file sia la condivisione di directory e più in
generale la condivisione di un sottoalbero.
Esaminiamo il caso più semplice, ovvero la condivisione dei file.
La soluzione più semplice ed immediata prevede la duplicazione del file, quindi tutti gli utenti che
vogliono condividerne uno creano una copia sotto la propria directory. Questo modo di operare è
però svantaggioso in quanto:
•
occupa spazio (se i file sono molti lo spreco diventa evidente);
•
quando un file viene aggiornato il sistema operativo si deve preoccupare di mantenere
aggiornate tutte le copie di quel file;
•
quando un file viene cancellato il sistema deve eliminare tutte le altre copie;
•
in fase di back up il sistema deve decidere quante copie del file conservare.
In alternativa alla duplicazione è possibile memorizzare delle informazioni in più (attributi) per ogni
file. Il sistema operativo mantiene per ogni file il nome del proprietario ed un contatore che indica il
numero di directory che condividono il file.
Alla creazione di un file, l’attributo “proprietario” viene inizializzato con la directory che contiene
il file mentre l’attributo “contatore” viene posto a 1.
Quando un’altra directory vuole condividere il file, il sistema inserisce nell’attributo locazione
(memorizzato nel descrittore del file all’interno della directory) la posizione del file sul disco e
incrementa l’attributo contatore.
Per illustrare un’altra soluzione al problema della condivisione delle informazioni occorre prima
introdurre il tipo di file link. Un link (o collegamento simbolico) è un puntatore ad un file (o ad una
directory) che può contenere il path name assoluto o relativo del file (o della directory), che può
essere così facilmente localizzato.
La condivisione si realizza inserendo il link nell’attributo locazione del file presente. Quindi nella
struttura di directory il sistema invece di specificare il puntatore alla posizione del file sul disco,
indica il path name del file da condividere.
Questo modo di operare realizza la struttura di directory a grafo. Poiché un file può avere più path
name assoluti, diversi nomi presenti nelle directory possono riferirsi allo stesso file. Il sistema deve
fare in modo che nessuna zona venga percorsa più di una volta nelle varie ricerche per evitare che si
creino dei loop. Esistono appositi algoritmi che evitano il formarsi di cicli all’interno della struttura
a grafo della directory.
Quando un file condiviso viene cancellato il sistema deve gestire i link associati. Per i link simbolici
(soft link) l’unico problema riguarda la successiva creazione di un altro file con lo stesso nome del
file cancellato e avente la stessa locazione su disco. In generale si lascia all’accortezza dell’utente
evitare che si crei questa situazione. In tutti gli altri casi i link simbolici rimangono pendenti.
Più complicata è la gestione degli hard link, cioè i link che riferiscono indirizzi sul disco. Il sistema
può tenere un contatore che indica il numero di link presenti per ogni file condiviso.
La cancellazione di un file è consentita solo quando il contatore ha valore 0.
A.C. Neve – Sistemi operativi 3.0
46
Struttura logica di una directory
Le directory sono delle tabelle di nomi di file con una serie di attributi.
Ogni entrata in tabella (quindi in directory) definisce un file.
Un file è identificato da un nome, dai suoi attributi e dai puntatori d’accesso per le operazioni di
read e write sui blocchi fisici.
Le directory sono tenute sul disco mentre le entrate in directory di un file aperto sono copiate in
memoria per velocizzare le operazioni.
Per uniformare le operazioni del file system, anche le directory stesse sono considerate come file.
La directory primaria, detta root (radice), viene posta ad un indirizzo predefinito del disco in modo
che l’accesso non richieda ricerche e sia quindi immediato.
Ogni volta che si inizializza un volume, viene automaticamente creata una directory root.
Alla creazione di un nuovo file viene ricercata una riga vuota nella tabella della directory che viene
riempita con le informazioni riguardanti il nuovo file. Esiste quindi una corrispondenza univoca tra
le righe della tabella della directory ed i file.
Esiste poi una directory dei nomi dei file FND (File Name Directory) il cui scopo è quello di
definire una corrispondenza tra i nomi dei file ed un identificatore del file fisico utilizzato dal
sistema per puntare fisicamente il file.
Quando un utente richiede l’accesso ad un file si ricerca il nome del file nella FND per determinare
il corrispondente identificatore che sarà poi utilizzato per localizzare il file stesso nella directory.
Poiché le directory possono avere lunghezza variabile, la fine di una directory viene marcata con un
carattere standard EOF (End Of File).
Quando vengono creati nuovi file si usano le prime righe vuote della directory altrimenti si
aggiungono nuove righe.
Le operazioni più frequenti che si effettuano sulle directory sono: ricerca, inserimento e
cancellazione di righe. Se l’organizzazione della directory è in forma di lista lineare, il numero di
confronti per la ricerca sarà proporzionale al numero stesso di righe. Per migliorare le prestazioni si
usa organizzare la directory secondo un albero binario bilanciato che richiederà un numero di
confronti proporzionale al logaritmo in base 2 del numero di righe.
Interfaccia utente
E’ l’interfaccia per mezzo della quale gli utenti possono effettuare operazioni sui file per mezzo di
comandi del sistema operativo. Le operazioni possibili sui file vengono eseguite tramite system call
e variano in funzione del tipo di sistema operativo utilizzato. Quando è richiesta una operazione di
read o write il sistema deve individuare l’elemento di directory nella struttura tramite il nome del
file passato come parametro della system call.
I comandi generali per la gestione dei file possono essere:
•
•
•
•
•
•
•
•
•
Create: alloca un elemento nella struttura di directory
Delete: cancellazione di un file
Open: apre il file per effettuare successivamente delle operazioni sul file
Close: chiude il file
Write: scrive dei dati nel file
Read: legge i dati dal file
Copy: copia il file in un altra directory
Rename: modifica il nome del file
Attribute: modifica gli attributi del file
A.C. Neve – Sistemi operativi 3.0
47
Oltre alle operazioni sui file sono disponibili dei comandi per operazioni sulle directory:
•
•
•
•
Dir: elenca i nomi dei file contenuti nella directory
Make dir: crea una directory con un certo nome
Remove dir: elimina una directory (se vuota)
Change dir: cambia directory di lavoro
Esistono poi i comandi per la gestione dei volumi:
•
•
•
•
•
•
•
Initdisk: inizializza i volumi mai usati in precedenza creando una radice con attributi
Mount: avvisa il sistema operativo dell’avvenuta istallazione di un nuovo volume
Dismount: avvisa il sistema operativo della prossima rimozione di un volume
Verify: verifica l’integrità fisica del volume
Backup: salvataggio, su di un’altra unità, del contenuto di un volume
Restore: ripristino del contenuto di un volume in precedenza salvato
Defrag: ottimizza l’allocazione dei file in un volume
Modello tipizzato
Con il termine tipizzazione si intende la capacità del sistema operativo di distinguere tra i vari tipi
di file potendo così automatizzare la loro apertura dal relativo software applicativo.
Il metodo più semplice consiste nel corredare il nome del file di una estensione che,
mnemonicamente, ne ricordi la tipologia (exe, txt, doc, obj, jpg, hlm ecc.).
Un altro metodo, meno utile all’utente, consiste nel codificare la tipologia del file all’interno del file
stesso. Naturalmente la tipizzazione rende più complesso il lavoro del sistema operativo.
Allocazione
Allocazione a blocchi
Un disco è suddiviso in partizioni, ognuna delle quali rappresenta un disco virtuale. In generale una
partizione indica una parte del disco, ma, in alcuni sistemi operativi, può anche accadere il
contrario, cioè esistono partizioni che occupano più dischi.
In generale esistono diverse partizioni per separare le informazioni dai programmi. Una partizione è
dedicata all’installazione del sistema operativo e dei programmi, un’altra memorizza i dati utilizzati
dai programmi. Con una organizzazione di questo tipo, se si verifica un crash del sistema la
partizione dei dati è salva.
Inoltre la suddivisione del disco in partizioni permette di installare più sistemi operativi su una
stessa macchina, ognuno su partizioni diverse.
La prima partizione contiene sempre il file system.
Il primo blocco contiene il master boot record (MBR), utilizzato per l’avvio della macchina.
Il secondo blocco contiene la tabella delle partizioni che memorizza le informazioni sulle partizioni
del disco; per ogni partizione viene indicato l’inizio e la fine. All’interno della tabella una partizione
è contrassegnata attiva: questa è la partizione che contiene il sistema operativo che deve essere
caricato su quella macchina.
All’avvio della macchina parte il programma bios che esegue il MBR e carica la tabella delle
partizioni, indicando anche la partizione attiva. Quindi viene caricato il sistema operativo tramite
l’esecuzione di un apposito programma presente nella partizione attiva. Da questo momento in poi
la macchina è sotto il controllo del sistema operativo.
A.C. Neve – Sistemi operativi 3.0
48
Nei sistemi basati sull’uso della memoria virtuale esiste una partizione (la seconda) dedicata
all’area di swap, la cui grandezza dipende dal tipo di programmi che vengono eseguiti nel sistema.
Poiché è difficile da dimensionare, si definisce uno spazio di swap grande due volte lo spazio di
memoria RAM.
Il File System ha anche il compito di gestire lo spazio della memoria secondaria che viene
considerata come un insieme lineare di blocchi.
Il modulo di I/O si interessa poi della gestione degli indirizzi fisici.
Il File System deve mantenere traccia di tutti i blocchi del disco allocati ad un certo file e di tutti i
blocchi liberi e disponibili per altre allocazioni.
Le strategie utilizzate per l’allocazione dello spazio disco dovranno cercare sempre di ottimizzare
alcuni dei seguenti fattori:
•
•
•
•
velocizzazione di tutte le operazioni di accesso ai file e di allocazione e disallocazione, questo
parametro viene valutato con il numero di accessi al disco per identificare un blocco
utilizzazione del trasferimento multisettore e multitraccia per ridurre i tempi di accesso
ottimizzazione dell’utilizzo dello spazio disco per ridurre la frammentazione esterna
impegno di CPU e di memoria centrale per la gestione delle tabelle di traccia
Il problema della gestione del disco è molto simile a quello della memoria centrale ma con tempi
molto più elevati per il disco. Come nel caso della memoria centrale, si potranno anche ora
utilizzare le diverse strategie di allocazione contigua e non contigua.
Allocazione contigua
L’allocazione contigua è quella più semplice. L’idea di base consiste nell’allocare il file sul disco
assegnandoli un certo numero di blocchi contigui secondo lo schema tipico dell’array lineare.
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Start Block
15
16
Block 5
20
Block 1
Block 2
Block 3
Block 4
17
18
19
End Block
21
22
23
24
L’indirizzo iniziale e la dimensione del file sono memorizzati nella directory e sono sufficienti per
accedere a qualsiasi blocco del file. L’accesso sequenziale è molto semplice e veloce poiché i
blocchi sono adiacenti sia logicamente che fisicamente favorendo così il trasferimento multisettore.
Altrettanto semplice e veloce risulta anche l’accesso casuale poiché l’indirizzo fisico del generico
blocco può essere calcolato dall’indirizzo di partenza del file memorizzato nella directory.
Per es., l’indirizzo del blocco 2 si può calcolare sommando all’indirizzo dello Start Block (10) un
displacement pari a 2 ottenendo l’indirizzo del Block 2 (12).
Gli indirizzi e le dimensioni delle aree libere sul disco sono mantenuti in una lista separata e
memorizzati nelle entrate non usate della directory.
A.C. Neve – Sistemi operativi 3.0
49
In fase di allocazione, dovranno essere cercate delle aree libere, la scelta di queste aree potrà essere
effettuata con i classici algoritmi di best fit o first fit.
Il problema della frammentazione interna non è particolarmente critico mentre lo è quello della
frammentazione esterna in quanto si possono venire a determinare numerosi gruppi di blocchi liberi
aventi dimensioni totali troppo piccole per essere utilizzati ma che complessivamente rappresentano
uno spazio disco rilevante.
Questo problema può essere risolto con una periodica operazione di deframmentazione del disco.
L’allocazione contigua richiede che le dimensioni del file siano note in anticipo e questo non
sempre è possibile come nel caso di file che possono aumentare di dimensione nel tempo.
Si ipotizzi che un file aumenti le sue dimensioni dopo che il sistema lo ha allocato. Se l’allocazione
è contigua ed è libero il blocco successivo all’ultimo allocato a quel file, il sistema estende
l’allocazione normalmente apportando le opportune modifiche agli attributi del file.
Se il blocco successivo è occupato da un altro file, per evitare di abortire il programma, il sistema
deve trovare un insieme di blocchi contigui liberi in grado di soddisfare la nuova dimensione del
file e spostare tutto il file nel buco individuato, rilasciando quello vecchio.
Con questo tipo di organizzazione il sistema impiega molto tempo nel trasferimento dei file da una
parte all’altra del disco.
Una alternativa consiste nel gestire il problema preventivamente, allocando il file in uno spazio
maggiore di quello richiesto prevedendo le sue variazioni nel tempo.
Allocazione concatenata
Con l’allocazione dei file a lista concatenata non è più richiesta la contiguità dei blocchi.
Il sistema alloca i blocchi liberi ovunque trovi spazio libero all’interno del disco e li collega tramite
l’utilizzo di puntatori.
0
1
5
6
2
3
7
8
8
10
Start
Block
15
20
Block 1
11
4
9
9
12
Block 2
13
Block 3
14
Block 4
17
18
19
22
23
24
12
6
16
16
End
Block
21
e
n
d
In ogni blocco vengono riservati alcuni byte per definire il puntamento al blocco successivo.
L’ultimo blocco contiene l’indicatore di terminazione del file.
Nella directory, il file è individuato dal puntatore iniziale corrispondente all’indirizzo, sul disco, del
primo blocco.
L’accesso ad un file di questo tipo deve necessariamente iniziare dal primo blocco il cui indirizzo è
contenuto nella directory e proseguire attraverso tutti i blocchi fisici utilizzando i puntatori ai
blocchi successivi. Questo modo di operare facilita notevolmente l’accesso sequenziale in quanto il
sistema individua l’indirizzo del primo blocco contenuto nella struttura di directory mentre gli
indirizzi dei blocchi successivi li individua scorrendo i blocchi.
Più complesso è invece l’accesso multiblocco in quanto necessita della conoscenza anticipata del
numero di blocchi che dovranno essere trasferiti e dei loro indirizzi.
A.C. Neve – Sistemi operativi 3.0
50
L’incremento delle dimensioni del file avviene in modo abbastanza semplice in quanto basta
allocare un qualsiasi altro blocco libero e concatenarlo con quello che era l’ultimo.
La disallocazione avviene invece contemporaneamente su tutti i blocchi.
Questo tipo di allocazione non soffre del problema della frammentazione: quella esterna non esiste
perché il sistema recupera tutti i blocchi liberi, mentre quella interna è presente solo nell’ultimo
blocco allocato ad ogni file, come accade con qualsiasi metodo di allocazione.
La deframmentazione è comunque utile in quanto migliora il tempo di accesso.
Il problema fondamentale di questo tipo di allocazione riguarda lo spazio occupato dai puntatori.
Per ogni blocco occupato il sistema deve mantenere un puntatore al blocco successivo: ciò implica
minor spazio disponibile per la memorizzazione dei dati, quindi lo spazio su disco non è
efficientemente sfruttato.
Lo svantaggio di questa tecnica è costituito dalla lentezza degli accessi casuali e dall’impossibilità
di utilizzare il trasferimento multiblocco. Un altro svantaggio riguarda l’eventuale possibilità di
danneggiamento di un blocco che implica l’impossibilità di accesso a tutti i blocchi successivi.
Allocazione indicizzata
L’indicizzazione è il metodo più efficiente di allocazione non contigua sul disco.
Utilizza ancora il sistema dei puntatori ai blocchi allocati ma migliora il tempo dell’accesso casuale
raggruppando i puntatori nel primo blocco detto i-node (index block).
La directory dei nomi, anziché contenere l’indirizzo del primo blocco, contiene l’indirizzo del
blocco indice. L’indicizzazione elimina così le differenze tra i tempi di accesso esistenti tra
l’accesso sequenziale e l’accesso casuale.
0
1
2
3
4
5
6
Start
Block
11
7
8
9
12
Block 1
13
Block 2
14
10
Index
Block
6
8
9
12
16
Block 3
15
16
17
18
19
20
End Block
21
22
23
24
Ovviamente ad ogni file deve essere allocato un numero maggiore di blocchi rispetto alle richieste:
un file composto da k blocchi, viene allocato in (k+1) blocchi fisici. Quindi la presenza del blocco
indice comporta l’introduzione di overhead che risulta maggiore per i file di dimensioni minori.
Inoltre un file con dimensione molto estesa può richiedere un numero elevato di blocchi indice.
Per esempio, facendo uso di blocchi da 2048 byte e puntatori da 4 byte, potranno essere indicizzati
soltanto 512 blocchi (nel blocco indice si potranno inserire 2048/4 puntatori) corrispondenti ad una
dimensione totale del file di 512*2048 = 1 megabyte che potrebbe non risultare sufficiente.
Si risolve questo problema facendo uso dell’indicizzazione multilivello: il blocco indice di primo
livello viene usato per puntare ad un blocco indice di secondo livello il quale, a sua volta, è usato
per puntare ai blocchi di dati.
Utilizzando l’indicizzazione a due livelli dell’esempio precedente si avrà: un primo blocco indice
che punta a 512 blocchi indice di secondo livello, ognuno dei quali punterà a 512 blocchi dati per
un totale di 524 megabyte.
Per migliorare i tempi di risposta si mantengono in memoria i blocchi indice di uso recente e
frequente come pure i blocchi indice dell’insieme dei blocchi liberi contigui.
A.C. Neve – Sistemi operativi 3.0
51
FAT (File Allocation Table)
Per ridurre la percentuale di spazio utilizzato per la gestione della lista concatenata è stato introdotta
l’allocazione a lista concatenata a cluster. Un cluster è costituito da un gruppo di blocchi contigui.
Se per la memorizzazione di un file serve un numero maggiore di blocchi di quelli contenuti in un
cluster, il sistema collega più cluster tramite puntatori. La lista concatenata è quindi gestita a cluster.
Adottando questo metodo il sistema risolve il problema dell’overhead introdotto dai puntatori
nell’allocazione a blocchi poiché il numero dei puntatori è notevolmente ridotto.
Introduce però frammentazione interna perché aumenta la quantità di disco inutilizzato relativo
all’ultimo cluster. La dimensione del cluster è maggiore della dimensione del blocco fisico; un
cluster può essere paragonato ad un blocco molto grande: se però aumenta la dimensione del blocco
aumenta la frammentazione interna.
Nell’allocazione concatenata (a blocchi o a cluster) per leggere un file la testina si deve spostare
molte volte: il sistema ricava dalla struttura di directory l’indirizzo del primo blocco su cui è
allocato il file e vi accede; quindi tramite il puntatore del primo blocco individua il secondo blocco
e così via finché arriva alla fine. In generale i blocchi di un file si trovano su tracce diverse. Per
ottimizzare e diminuire il numero di spostamenti della testina il sistema utilizza la file allocation
table (FAT), una tabella presente all’inizio di ogni partizione (quindi sul disco) in cui il numero di
entrate corrisponde al numero di blocchi totali presenti sul disco. E’ indirizzata tramite il numero
del primo blocco che il sistema può estrarre dalla struttura di directory ed ogni entrata contiene
l’indirizzo del blocco successivo che compone il file, oppure contiene il valore “null” o un codice di
“end of file” se è l’ultimo blocco del file. I blocchi liberi sono indicati con 0.
Elemento della struttura di directory
Name
Start
...
FILE.doc
1
...
blocco libero
1
2
3
4
5
6
7
8
9
10
…
FAT
4
…
…
9
…
…
0
…
null
…
...
La FAT permette di realizzare la concatenazione dei blocchi allocati ai file, senza memorizzare
questi puntatori all’interno del blocco stesso migliorando la velocità e consentendo la duplicazione
della FAT.
La presenza della FAT rende più veloce l’accesso diretto ai file: gli spostamenti della testina sono
minori, le letture avvengono sullo stesso blocco o su blocchi appartenenti alla stessa traccia.
La FAT è soggetta a modifiche, dovute al cambiamento dello stato di allocazione di un disco. Ogni
volta il sistema deve accedere al disco (supporto di memorizzazione ad accesso lento) per operare
sulla tabella. Per rendere più veloci gli accessi il sistema mantiene una copia della FAT in memoria
(se il numero dei blocchi che compongono il disco è elevato solo una parte sarà copiata in memoria)
alla quale accede ogni volta che è richiesto e periodicamente trasferisce le modifiche dalla memoria
al disco. Nel caso in cui si verificasse un crash del sistema quando non fosse ancora avvenuto
A.C. Neve – Sistemi operativi 3.0
52
l’aggiornamento della tabella sul disco, tutte le modifiche fatte dall’ultimo accesso alla FAT sul
disco verrebbero perse, quindi la tabella presente sul disco non sarebbe più attendibile.
La FAT è memorizzata sul disco ed ha una entrata per ogni cluster del volume con un codice che
indica il numero del cluster successivo.
Codice FAT
0000H
0002H – FFEFH
FFF0H – FFF6H
FFF7H
FFF8H – FFFFH
Cluster libero
Indirizzo cluster successivo
Cluster riservato
Cluster guasto
Ultimo cluster di un file
Il massimo numero di cluster dipende dalla dimensione dell’entrata della FAT
Cluster Size Dim Volume
FAT 12
0.5 KB
2 MB
1 KB
4 MB
2 KB
8 MB
4 KB
16 MB
8 KB
16 KB
32 KB
Dim Volume
FAT 16
Dim Volume
FAT 32
128 MB
256 MB
512 MB
1024 MB
2048 MB
1 TB
2 TB
2TB
2 TB
Cluster di grandi dimensioni ottimizzano i tempi di I/O ma aumentano la frammentazione interna.
L’organizzazione della FAT di un volume è fatta in modo tale che:
•
•
•
•
il primo cluster contiene il bootstrap con le informazioni sul volume
dopo è presente la FAT che fornisce la lista dei puntatori ai cluster e la lista dello stato dei cluster
il resto del volume contiene i cluster dei file o directory allocati
il primo file del volume è detto root directory e genera l’albero gerarchico
Il boot record contiene le seguenti informazioni:
•
•
•
•
•
istruzione di salto all’inizio del codice di boot
descrizione del volume (BIOS Parameter Block)
codice di bootstrap
eventuale tabella delle partizioni
error checking
Il BIOS Parameter Block contiene invece le seguenti informazioni:
•
•
•
•
•
•
•
dimensioni del settore fisico
numero di settori per traccia
numero di tracce per cilindro
numero totale di settori del volume
numero di settori per cluster
numero di settori riservati
numero di entrate della root directory
A.C. Neve – Sistemi operativi 3.0
53
L’utilizzo delle partizioni del disco ha determinato un cambiamento del boot record.
Il primo settore del disco viene ora detto MBR (Master Boot Record) che include la FDISK table ed
il codice di bootstrap della partizione attiva dalla quale legge il primo settore detto PBR (Partition
Boot Record) che è organizzato come in precedenza esposto.
Nella FAT 32, una directory è un file contenente una serie di entrate a 32 byte ognuna delle quali
descrive un file facendo uso della seguente struttura:
N° del byte
0 – 10
11
12 – 21
22 –23
24 – 25
26 – 27
28 – 31
Descrizione
Nome corto del file SFN (Short Filename): 8 byte per il nome e 3 per l’estensione
Se è più corto si aggiungono degli spazi. Il punto non si memorizza.
Attributi: sono usati solo 6 bit con i seguenti significati
Flag Archivio (settato se il file è stato modificato dopo l’ultimo backup)
Flag Only read (non può essere modificato)
Flag System ( file di sistema)
Flag Hidden (file nascosto che non compare nei file della directory)
Flag Directory (indica che il file è una directory)
Flag Volume (indica che non è un file ma una etichetta di volume)
Si usano solo nella FAT 32 e contengono le seguenti informazioni:
12: riservato
13: millisecondi dell’orario di creazione
14,15: orario di creazione
16,17: data di creazione
18,19: data dell’ultimo accesso
20,21: parte più significativa del numero del primo cluster
Orario dell’ultima modifica
Data dell’ultima modifica
Parte meno significativa del numero del primo cluster
Dimensione del file (4Gbyte)
A.C. Neve – Sistemi operativi 3.0
54
Glossario
background
batch
BIOS
(Basic I/O System)
bootstrap
boundless
buffer
cluster
context switch
deadlock
dispatcher
displacement
embedded
foreground
fork
interleaving
interrupt
join
Sfondo. Continuamente presente. Termine applicato ad un processo a
bassa priorità che viene eseguito quando la risorse non sono usate per
altre applicazioni. Processi in stato di sospensione pronti però ad
attivarsi per fornire servizi (utility, antivirus ecc).
Tipo di sistema operativo che prevede l’esecuzione di un programma
alla volta sempre residente nella memoria: il calcolatore legge un
programma, lo carica in memoria e lo esegue fino alla fine o fino
all’errore, poi stampa il risultato. Passa poi ad un successivo programma
Insieme di routine contenute nella ROM utilizzate all’avviamento del
sistema, per le comunicazioni tra gli elementi base del sistema e per il
settaggio delle varie parti.
Tecnica di trasferimento di un grosso programma in memoria centrale
che consiste nel caricamento di un ristretto numero di istruzioni
(bootstrap loader) prelevate da una ROM le quali sono poi usate per
caricare il resto del programma. Questa tecnica è tipicamente usata nella
fase iniziale di avviamento di un computer.
Senza limiti. I/O bound o CPU bound sono detti i processi che fanno un
uso elevato di I/O o di CPU
Cuscinetto. Area di memoria usata per contenere, temporaneamente, dei
dati. Viene utilizzata per facilitare il trasferimento di dati tra dispositivi
aventi differenti velocità o non sincronizzabili.
Grappolo. Gruppo di settori di un hard disk costituenti una singola unità
di trasferimento dati.
Commutazione di contesto. Indica il passaggio della CPU da un
processo ad un altro con il salvataggio dei registri del processo sospeso
ed il ripristino di quello attivato. Viene effettuato dal dispatcher.
Punto morto. Indica un conflitto non risolvibile. Condizione di
impedimento dell’esecuzione di due processi in attesa di una risorsa
posseduta dall’altro. Condizione di stallo.
Colui che spedisce. E’ un modulo del sistema operativo che attua il
cambio di contesto.
Spostamento, spiazzamento. Differenza numerica tra due valori uno dei
quali è un valore fisso o di riferimento.
Incluso, incassato. Riferito a computer inclusi in sistemi la cui attività
non rientra in quella tipica dei sistemi di elaborazione.
In primo piano. Termine applicato ad un processo ad alta priorità che
interagisce con l’utente ed è, quindi, in primo piano.
Biforcazione. Operazione di creazione di un nuovo processo.
Fenomeno dovuto al fatto che, durante il trasferimento, il disco continua
a ruotare ed il successivo settore passa sotto la testina mentre è in corso
il trasferimento.
Richiesta asincrona di asservimento. Determina la sospensione del
processo in corso di esecuzione per trasferirlo ad un altro. Generalmente
la richiesta proviene da un device esterno ma può essere anche generata
da una condizione di errore software.
Unione, giuntura. Operazione usata per unificare due sequenze di codice
sdoppiate da una fork.
A.C. Neve – Sistemi operativi 3.0
55
latency time
link
locality
MBR
(Master Boot Record)
overhead
path name
PCB
(Process Control Block)
polling
preemptive
race condition
scheduling
seek time
sezione critica
spooling
Termine usato per indicare il ritardo tra l’inizio di una richiesta e la
ricezione del dato richiesto. Generalmente indica il tempo che occorre
per trovare il settore all’interno della traccia di un disco.
Collegamento. Connessione logica tra due differenti oggetti sia
hardware che software.
Vicinanza. Termine utilizzato per descrivere la velocità di accesso al
codice eseguibile da parte di un programma e la relativa disponibilità
dello stesso nei vari livelli di memoria. Può essere inteso sia in termini
spaziali che temporali.
Rappresenta il primo settore di un hard disk che contiene il codice di
bootstrap della partizione attiva necessario per l’avviamento.
In alto, sopraelevato. Termine utilizzato per indicare la quantità di
tempo dedicato all’esecuzione di funzioni di sistema operativo piuttosto
che all’esecuzione di programmi di utente.
Nome del percorso che deve essere seguito per raggiungere un file
all’interno di una gerarchia di directory. In una rete indica il percorso
che un messaggio deve effettuare tra i vari nodi della rete.
E’ una struttura dati mediante la quale il sistema operativo gestisce un
processo e ne rappresenta lo stato globale. Per poter sospendere e poi
riattivare un processo è necessario poter ricordare le condizioni in cui si
trovava all’atto della sospensione. Questo insieme di informazioni è
chiamato stato del processo ed è costituito da tutte le informazioni
necessarie per poter riprendere l’esecuzione dopo una sospensione
Sondaggio, scrutinio, interrogazione sequenziale. Procedura di
interrogazione ciclica di un sequenza di periferiche per verificare
l’esistenza di una richiesta di asservimento.
Riferito ad un processo, si dice preemptive quando è possibile togliere la
CPU ad un processo in relazione ad eventi esterni al processo stesso e
cioè può essere sospeso anche se dispone di tutte la risorse necessarie.
Con riferimento ad una risorsa,di dice preemptive se può essere sottratta
al processo senza conseguenze.
Situazione che si verifica quando più processi accedono e manipolano
gli stessi dati concorrentemente e l’esito varia a seconda dell’ordine con
il quale sono avvenuti gli accessi.
Catalogare. Si intende per scheduling il criterio di individuazione, di
uno tra più richiedenti, al quale assegnazione l’esecuzione di un sevizio.
Operazione che, generalmente, consistente nella determinazione
dell’ordine secondo il quale un insieme di programmi dovrà essere
eseguito.
In un hard disk è il tempo impiegato per l’individuazione della traccia
che contiene il blocco fisico cercato; questo tempo è proporzionale alla
distanza che la testina deve percorre ad ogni spostamento.
Parte di programma dalla quale si può accedere a risorse comuni o
modificare dati comuni ad altri processi. Ad un solo processo per volta è
consentito eseguire la propria sezione critica: l’esecuzione delle sezioni
critiche da parte dei processi deve essere mutuamente esclusiva nel
tempo, cioè, quando un processo opera in sezione critica, nessun altro
processo può fare altrettanto.
Operazione consistente nell’uso dell’hard disk per il trasferimento dati
da memoria centrale a dispositivi periferici più lenti migliorando
l’efficienza d’uso della CPU. Molto usato per i processi di stampa.
A.C. Neve – Sistemi operativi 3.0
56
starvation
swapping
thrashing
thread
throughput
timesharing
transfer time
turnaround
Condizione per la quale un processo non riesce ad iniziare o completare
la sua esecuzione perché continuamente sospeso da altri a priorità
superiore.
Scambio. Trasferimento di parti (pagine) di programma da o per l’hard
disk durante l’esecuzione a causa della mancanza di spazio libero in
memoria centrale.
Bastonatura. Fenomeno per il quale il sistema tende a far aumentare
notevolmente la frequenza di page fault, il sistema cioè trascorre gran
parte del suo tempo in operazioni di swapping di pagine con un impegno
di CPU apparentemente basso ma con un notevole degrado delle
prestazioni del sistema.
Unità di base di utilizzo della CPU. Un thread è una parte di un processo
che può essere eseguita contemporaneamente ad altri thread riuscendo
così ad aumentare lo pseudoparallelismo.
Portata. Quantità di lavoro (istruzioni) eseguita nell’unità di tempo. Nei
sistemi di comunicazione è la quantità di dati trasmessi nell’unità di
tempo.
Suddivisione del tempo di CPU tra più processi
Tempo impiegato per la lettura da una unità ed il trasferimento in
un’altra unità del singolo blocco di dati.
Cambiamento di direzione. Tempo di elaborazione delle richieste di
pagina.
A.C. Neve – Sistemi operativi 3.0
57