Sei sulla pagina 1di 96

APPUNTI DI SISTEMI OPERATIVI

Russo Alessio - Savaia Gianluca


Versione 1.0a Data di pubblicazione: 04/07/2013

Contatti: russo.alessio@outlook.com gianluca.savaia@gmail.com

Russo Alessio, Savaia Gianluca Sistemi Operativi - 1


Prefazione

Russo Alessio, Savaia Gianluca Sistemi Operativi - 2


INDICE

PREFAZIONE...........................................................................................................................................2

COMPUTER HARDWARE E CONCETTI BASE.............................................................................................5

IL SISTEMA OPERATIVO IN BREVE 5


SOMMARIO HARDWARE 5
CPU 5
MEMORIA 5
INPUT/OUTPUT BUS DI SISTEMA 6
CONCETTI BASE DEI PROCESSI 7
SYSTEM CALLS 7
TIPI DI SISTEMI OPERATIVI 8

PROCESSI..............................................................................................................................................10

MODELLO A PROCESSI SEQUENZIALI 10


CREAZIONE E TERMINAZIONE DEI PROCESSI 10
GERARCHIA DEI PROCESSI 11
STATO 12
IMPLEMENTAZIONE DEI PROCESSI 12

THREADS..............................................................................................................................................14

IL MODELLO A THREAD 14
IMPLEMENTAZIONE DEI THREADS 14
USER VS KERNEL SPACE 15
SCHEDULER ACTIVATIONS 16
POP-UP THREADS 16
USO DEI THREADS 16

INTERPROCESS COMMUNICATION........................................................................................................19

RACE CONDITION & CRITICAL REGIONS 19


MUTUA ESCLUSIONE - BUSY WAITING 20

Russo Alessio, Savaia Gianluca Sistemi Operativi - 3


DISABILITARE GLI INTERRUPTS 20
VARIABILI DI LOCK 20
ALTERNANZA STRETTA (STRICT ALTERNATION) 21
SOLUZIONE DI PETERSON 22
ISTRUZIONE TSL 23
MUTUA ESCLUSIONE - SENZA BUSY WAITING 24
SLEEP & WAKEUP 25
SEMAFORI 26
MUTEX 28
MONITORS 29
MESSAGE PASSING 29
BARRIERS 29

SCHEDULING.........................................................................................................................................31

QUANDO FARE LO SCHEDULE 31


CATEGORIE ALGORITMI DI SCHEDULING IN BASE AL SISTEMA 31
SISTEMI BATCH 32
FCFS FIRST-COME FIRST-SERVED 33
SJF SHORTEST JOB FIRS 33
SHORTEST REMAINING TIME NEXT 33
SISTEMI INTERATTIVI 34
ROUND-ROBIN 34
PRIORITY SCHEDULING 34
CODE MULTIPLE 35
SPN SHORTEST PROCESS NEXT 35
SISTEMI REAL-TIME 36
THREAD SCHEDULING 36

PROBLEMI CLASSICI DELL'IPC................................................................................................................38

PRODUTTORE CONSUMATORE CON PTHREAD 38


FILOSOFI A CENA 39
LETTORI E SCRITTORI 42

MEMORY MANAGEMENT.....................................................................................................................46

BASE E LIMIT REGISTER 46


SWAPPING 46

Russo Alessio, Savaia Gianluca Sistemi Operativi - 4


MEMORIA VIRTUALE 46
PAGING 46
PAGE TABLE 48
TLB 48
PAGINAZIONE A PI LIVELLI 48
INVERTED PAGE TABLE 48
PAGE REPLACEMENT 48
ALGORITMO OTTIMALE 49
NRU 49
FIFO 49
SECOND CHANCE 49
CLOCK 49
LRU 49
NFU 49
AGING 49
WORKING SET 49
WSCLOCK 50
NOTE GENERALI 50
LOCALE VS GLOBALE 50
THRASHING 50
DIMENSIONE PAGINA 50
CONDIVISIONE 50
LIBRERIE CONDIVISE 50
MAPPED FILES 50
BLOCCARE LE PAGINE 50
SEGMENTAZIONE 50

FILE SYSTEM..........................................................................................................................................51

FILES 51
STRUTTURA 51
ACCESSO 52
DIRECTORIES 52
IMPLEMENTAZIONE 52
DISCO 52
FILE 52
ALLOCAZIONE CONTIGUA 52
LINKED LIST 52
FILE ALLOCATION TABLE 53
I-NODES 53

Russo Alessio, Savaia Gianluca Sistemi Operativi - 5


DIRECTORIES 53
BLOCCHI LIBERI 53
CONSISTENZA 54
OTTIMIZZAZIONE 54

INPUT/OUTPUT....................................................................................................................................56

DMA (DIRECT MEMORY ACCESS) 56


GESTIONE DELLE INTERRUZIONI 56
I/O SOFTWARE 56
PROGRAMMED I/O 57
INTERRUPT-DRIVEN I/O 57
DMA I/O 57
SOFTWARE LAYER 57
DEVICE INDIPENDENTI DAL SOFTWARE I/O 58
USER SPACE I/O SOFTWARE 59
DISCHI 60
MAGNETIC DISKS 60
RAID - REDUNDANT ARRAY OF INEXPENSIVE DISKS 61
FORMATTAZIONE DEL DISCO A BASSO LIVELLO 62
STRUTTURA DI UN SETTORE 62
CYLINDER SKEW 62
INTERLEAVING 62
FORMATTAZIONE DEL DISCO AD ALTO LIVELLO 62
ALGORITMI DI SCHEDULING PER IL BRACCIO 63
GESTIONE DEGLI ERRORI 63
STABLE STORAGE 64

DEADLOCKS..........................................................................................................................................66

RISORSE 67
RISORSE PRERILASCIABILI - NON PRERILASCIABILI 67
ACQUISIZIONE DELLA RISORSA 68
RESOURCE DEADLOCKS - CONDIZIONI PER LO STALLO 68
MODELLI PER LO STALLO 68
IGNORARE LO STALLO - ALGORITMO DELLO STRUZZO 69
RICONOSCERE LO STALLO ED ELIMINARLO 70
RICONOSCERE LO STALLO CON UNA SOLA RISORSA DI OGNI TIPO 70
RICONOSCERE LO STALLO CON PI RISORSE DI OGNI TIPO 70

Russo Alessio, Savaia Gianluca Sistemi Operativi - 6


ELIMINARE UNO STALLO 71
ELIMINARE LO STALLO TRAMITE PRERILASCIO 71
ELIMINARE LO STALLO TRAMITE ROLLBACK 71
ELIMINARE LO STALLO TRAMITE TERMINAZIONE DEI PROCESSI 71
EVITARE LO STALLO 72
TRAIETTORIE DELLE RISORSE 72
STATI SICURI E INSICURI 72
ALGORITMO DEL BANCHIERE PER UNA RISORSA 73
ALGORITMO DEL BANCHIERE PER RISORSE MULTIPLE 73
PREVENIRE LO STALLO 74
ELIMINARE LA CONDIZIONE DI MUTA ESCLUSIONE 74
ELIMINARE LA CONDIZIONE DI HOLD & WAIT 74
ELIMINARE LA CONDIZIONE NON PRERILASCIABILIT 75
ELIMINARE LA CONDIZIONE DI ATTESA CIRCOLARE 75
TABELLA RIASSUNTIVA DELLE CONDIZIONI 75

ESERCIZI................................................................................................................................................77

ESERCIZIO 1 77
ESERCIZIO 2 77
ESERCIZIO 3 77
ESERCIZIO 4 78
ESERCIZIO 5 79
ESERCIZIO 6 79
ESERCIZIO 7 80
ESERCIZIO 8 81
ESERCIZIO 9 81
ESERCIZIO 10 81
ESERCIZIO 11 81
ESERCIZIO 12 81
ESERCIZIO 13 81
ESERCIZIO 14 81
ESERCIZIO 15 81
ESERCIZIO 16 82
ESERCIZIO 17 82
ESERCIZIO 18 82
ESERCIZIO 19 82

RIFERIMENTI & BIBLIOGRAFIA..............................................................................................................84

Russo Alessio, Savaia Gianluca Sistemi Operativi - 7


Russo Alessio, Savaia Gianluca Sistemi Operativi - 8
Russo Alessio, Savaia Gianluca Sistemi Operativi - 9
Computer Hardware e concetti base

Il sistema operativo in breve


Il sistema operativo un gestore di tabelle . Cit. Baglietto.
sistema operativo quel software che gestisce l'hardware e permette ai programmatori di poter interagire con
quest'ultimo. Senza il sistema operativo (abbreviato O.S. Oppure S.O , operating system o
sistema operativo) ogni programmatore quando scriverebbe un programma dovrebbe leggersi
tutte le guide delle periferiche con cui dovr comunicare, come interagiscono, etc...insomma un
casino.

Sommario hardware
CPU
Il cervello di un computer la CPU, prende le istruzioni dalla memoria e le esegue. In generale
le CPU hanno molti registri, usati dai programmatori per poter eseguire i loro programmi.
Alcuni di questi registri, quelli non general purpose, sono i seguenti:
PC (Program counter): registro che punta all'indirizzo di memoria della prossima
istruzione da eseguire.
SP (Stack pointer): registro che punta all'indirizzo dello stack in memoria.
PSW (Program Status Word): questo registro contiene alcune informazioni riguardo il
processo: per esempio la priorit, bits di controllo, bits di condizione e se il processo in
User o Kernel mode (il significato sar spiegato pi avanti, almeno) . E' molto importante
nelle system calls e nell'I/O.
Senza dilungarci sulla CPU (trattato nel I semestre), possiamo dire cosa significa eseguire un
processo in kernel o user mode:
Kernel mode: Quando eseguito in kernel mode la CPU pu eseguire tutte le istruzioni
del suo Instruction Set e usare qualsiasi feature dell'hardware. Il O.S eseguito in kernel
mode.
User mode: i programmi utenti sono eseguiti in user mode, il quale permette alla CPU di
poter eseguire solo una parte dei comandi del suo Instruction Set. Genericamente in
questa modalit i programmi non possono interagire con l'hardware (devono passare dal
O.S). Naturalmente non possono modificare il PSW per passare in kernel mode.
Un programma in modalit utente per parlare con il O.S deve far uso delle system call, le quali
sono funzioni speciali che eseguono un'istruzione TRAP e invocano il O.S. Quello che fa una

Russo Alessio, Savaia Gianluca Sistemi Operativi - 10


TRAP di passare da user mode a kernel mode, passando il controllo al O.S (che gira in kernel
mode appunto). Una volta finita la system call il controllo torna al programma dell'utente.

Memoria

Il secondo componente pi importante la memoria. Idealmente la memoria molto veloce,


ampia ed economica. Realmente purtroppo non cos.

Tempo d'accesso Tipo memoria Capacit


~ 1 nsec Registri CPU ~ 1 KB
~ 2 nsec Cache (CPU o altri dispositivi) ~ 4 MB
~ 10 nsec Memoria primaria ~ 512 4096 MB
~ 10 msec Memoria secondaria ~ 200 1000 GB

In generale con memoria primaria si intende la RAM, mentre con memoria secondaria i dischi
magnetici (hard disks), nastri magnetici, etc...

Russo Alessio, Savaia Gianluca Sistemi Operativi - 11


Input/Output Bus di sistema
Terzo componente pi importante: le periferiche di input/output e il bus di sistema.
Attraverso il bus di sistema viaggiano tutti i segnali di controllo, i dati, etc...che vanno da un
componente all'altro del computer: fra le periferiche, il processore(la cpu) e la memoria.
In generale i dispositivi I/O consistono di due parti: un controllore e il dispositivo stesso.
Il controllore un chip (o pi) che permettono il controllo fisico del dispositivo. Il software che
parla al controllore , dandogli comandi e leggendo le risposet si chiama driver. Il driver per
poter essere usato deve essere installato nel O.S, per permettergli di girare in kernel mode (e
interagire con l'hardware).
L'input e output pu essere fatto in modi diversi:
1. Il programma utente esegue una system call, dopodich l'O.S vede se una chiamata a
funzione per una procedura di un driver e passa il controllo al driver apposito. Dopodich
il driver inizia l'operazione di I/O e aspetta, continuando a vedere se il dispositivo abbia
finito. Questo metodo si chiama Busy Waiting, e ha lo svantaggio di lasciare pieno
controllo della CPU al driver, il quale spreca tempo di clock continuando a vedere se il
dispositivo di I/O abbia finito l'operazione.
2. Il secondo metodo simile al precendete, ma arrivato allo step di quando il driver inizia
l'operazione di I/O, quest'ultimo dice alla periferica di inviare un interrupt quando ha
finito l'operazione. L'interrupt un segnale molto speciale: quando il dispositivo ha finito
l'operazione, esso manda un segnale di interrupt usando certe linee del bus di sistema
all'interrupt controller. L'interrupt controller se non sta gi processando un altro
interrupt passer l'informazione al processore di quanto avvenuto, dicendogli poi il
numero del dispositivo che ha inviato l'interrupt. Una volta che la CPU ha preso
l'interrupt, i registri sono salvati e messi nello stack (SP, PSW, ) e la CPU passa in
kernel mode. Generalmente la CPU non conosce come gestire la cosa, quindi utilizza il
numero che gli ha passato l'interrupt controller per trovare l'indirizzo in memoria dell'
interrupt handler ( la parte di memoria dove risiedono gli indirizzi degli interrupt
handler si chiama interrupt vector). L'interrupt handler fornito nel driver del
dispositivo e permette di gestire l'interrupt. Una volta che l'interrupt handler ha iniziato,
esso prende il controllo della cpu, salvando i registri nello stack e vedendo cosa
successo al dispositivo. Una volta finito tutto esso cede il posto al programma utente.

3. Il terzo metodo quello di usare un chip speciale: un chip DMA (Direct memory
access), il quale permette il controllo del flusso di bit tra la memoria e il controllore del
dispositivo, senza aver bisogno della CPU. La CPU dice al DMA quanti byte trasferire, il
dispositivo e gli indirizzi di memoria coinvolti. Una volta che il chip DMA ha finito, esso
genera un interrupt.
Solitamente se ci sono pi interrupt insieme, vince quello del dispositivo con priorit pi alta.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 12


Concetti base dei processi

Il processo un programma in esecuzione. Associato al processo c' uno spazio di


indirizzamento, una locazione di memoria da cui il processo pu leggere e scrivere. Questo
spazio contiene l'eseguibile del programma, i dati del programma e il suo stack. Inoltre
associato con ogni processo c' una serie di risorse: i registri (PC, SP,...), lista dei file
aperti,etc... Un processo fondamentalmente un involucro che contiene tutte le informazioni
necessarie per poter eseguire un programma.
A volte due processi devono comunicare fra loro: per prevenire che un messaggio venga perso,
il processo che ha inviato il messaggio richiede al O.S di avvertire l'altro processo dopo un tot di
secondi. Una volta che i secondi sono passati il O.S genera un alarm signal che lo invia al
processo selezionato. Questo segnale sospende il processo da qualunque cosa stesse
facendo, salva i suoi registri nello stack e inizia una routine (funzione) speciale per gestire i
segnali. I segnali sono l'analogo hardware degli interrupts.

System calls
La system call una funzione speciale che permette al programma in user mode di poter fruire
dei servizi di sistema (leggere un file, etc...).
Quando viene chiamata una system call vengono eseguiti i seguenti step:
1. Push nello stack dei parametri della chiamata a funzione
2. Viene eseguita la chiamata a funzione a una libreria (ancora in user mode)
3. La funzione chiamata, solitamente scritta in assembly, inserisce il numero della system
call in un registro dove l'O.S si aspetta di trovare il numero, poi esegue un'istruzione
TRAP, che fa passare da user mode a kernel mode, e inizia l'esecuzione delle istruzioni
da un indirizzo di memoria fissato nel kernel. La TRAP come fare una chiamata a una
funzione, ma ha due differenze: prima di tutto passa in kernel mode, secondo la trap non
fa un JUMP a una locazione di memoria arbitraria, ma a una zona di memoria fissata.
4. Una volta eseguita la TRAP si arriva in una zona di memoria di una funzione che in base
al numero salvato nel registro (vedi step 3) sceglie di eseguire il gestore delle chiamate
di sistema opportuno.
5. Una volta che il gestore ha finito esso ritorna all'istruzione successiva alla TRAP e torna
in user mode.
6. Il programma libera lo stack.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 13


Alcune system calls che esistono in Linux: http://asm.sourceforge.net/syscall.html (se togliete il
sys_ dal nome della funzione potreste ritrovare molte funzioni che vi risultano familiari).
Altro link utile: http://www.lxhp.in-berlin.de/lhpsyscal.html .

Tipi di sistemi operativi

Sistemi monolitici: sono quei sistemi operativi composti da un solo programma che
gira in kernel mode.
Sistemi a livelli: sono sistemi monolitici ma strutturati a livelli, e ogni livello comunica
solo quelli adiacenti.
Microkernels: con il sistema a livelli solitamente sono tutti nel kernel, ma questo non
necessario. L'idea di splittare (spezzettare) il programma unico del sistema operativo
in tanti moduli, di cui solo uno di questi, il microkernel, viene eseguito in modalit kernel,
e gli altri vengono eseguiti in modalit utente. In pratica il microkernel e quel modulo
essenziale per permettere la comunicazione hardware-software.
Esokernels : Nessuno meglio di uno sviluppatore sa come rendere efficiente l'uso dell'hardware
disponibile, quindi l'obiettivo dargli la possibilit di prendere le decisioni. Gli esokernels sono
estremamente piccoli e compatti, poich le loro funzionalit sono volutamente limitate alla
protezione e al multiplexing delle risorse.
I kernel "classici" (sia monolitici che microkernel) astraggono l'hardware, nascondendo le risorse
dietro a un livello di astrazione dell'hardware (hardware abastraction level o HAL) In questi
sistemi "classici", se ad esempio viene allocata della memoria, il programma non pu sapere in
quale pagina fisica questa verr riservata dal sistema operativo, e se viene scritto un file non c'
modo di sapere direttamente in quale settore del disco stato allocato. questo il livello di
astrazione che un esokernel cerca di evitare. Esso permette ad un'applicazione di richiedere aree
specifiche di memoria, settori specifici su disco e cos via, e si assicura solamente che le risorse
richieste siano disponibili e che le applicazioni vi possano accedere. (Preso da wikipedia)

Da notare che generalmente i sistemi operativi sono scritti in C.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 14


Stamattina sono malato, coff coff - Baglietto

Russo Alessio, Savaia Gianluca Sistemi Operativi - 15


Processi

Modello a processi sequenziali

Con questo modello tutto il software che viene eseguito sul computer organizzato come
un'insieme di processi sequenziali cooperanti.
Un processo solamente l'istanza di un programma in esecuzione, con i registri e le varie
risorse che potrebbero servire al programma: concettualmente ogni processo ha la sua CPU
virtuale. In realt la CPU passa da un processo all'altro molto velocemente
(multiprogramming) grazie al process scheduler: quel componente software che decide
quale processo deve venir eseguito.
Si dice a processi sequenziali perch viene eseguito un processo alla volta in sequenza.
Da ora in poi consideriamo calcolatori con una sola CPU (senno si parla di multiprocessor).

Russo Alessio, Savaia Gianluca Sistemi Operativi - 16


Creazione e terminazione dei processi
Esistono principalmente due modi in cui I processi vengono creati:
1. Inizializzazione del sistema: al boot del O.S alcuni processi vengono creati.
2. System call per la creazione di un processo (eseguita da un programma gi in
esecuzioen)
In ambiente UNIX esiste la fork. Questa chiamata a sistema duplica la core image (spazio di
indirizzamento), i file aperti, ecc.. del processo chiamante cosicch si avranno 2 processi in
esecuzioni identici: in pratica clona il processo originale, eccetto l'identificato del processo
(PID). Solitamente il processo figlio esegue subito una chiamata a sistema exec che permette
di sostituire la propria core image con quella del nuovo programma da eseguire.
La fork restituisce il PID del processo figlio, in questo modo si riesce ad avere una gerarchia di
processi (un albero). Nel processo figlio la fork restituisce 0.
Per la terminazione dei processi ci sono 4 possibili modi:
1. Uscita normale (volontaria) : uscita normale del programma.
2. Uscita per errore (volontaria) : il programma scopre da solo un errore e termina.
3. Uscita per errore fatale (involontaria) : solitamente un errore di programmazione, che il
programma non pu scoprire da solo (ovviamente).
4. Terminazione da parte di un processo esterno (involontaria) : un altro processo ha
invocato una system call per terminare questo processo.

Esempio della fork:


#include <unistd.h> /* libreria che contiene la system call fork() */

int main()
{
int pid = fork(); /* creo un processo clone */
if(pid == 0)
{
/* processo figlio */
}
else if(pid > 0)
{
/* processo padre */
}
else
{
/* Errore. */
}
return 0;
}

Russo Alessio, Savaia Gianluca Sistemi Operativi - 17


Gerarchia dei processi
Un processo crea dei processi figli, che a loro volta possono creare altri processi: si ottiene una
gerarchia di processi.
In UNIX un processo e tutti I suoi processi figli (creati con le fork) formano un process group.
Per esempio quando l'utente invia un segnale da tastiera, il segnale inviato a tutti I processi
del process group associati in quel momento con la tastiera.
In Windows tale concetto non esiste (di gerarchia dei processi), ovvero non esistono processi
padri e figli.

Stato
I processi una volta mandati in esecuzione, possono ritrovarsi in 3 stati:
1. RUNNING, se il processo sta impegnando la CPU.
2. BLOCKED, se il processo in attesa di un evento.
3. READY, se (in memoria) pronto per essere eseguito ( un altro processo in
esecuzione pero).
Un nuovo processo viene caricato in memoria dove nello stato READY ovvero pronto per
essere eseguito. Successivamente quando lo scheduler decide di assegnargli la CPU passa
allo stato RUNNING. A questo punto possono accadere diverse cose:
1. se il processo richiede accesso allI/O o comunque necessita di un evento per poter
continuare la sua esecuzione, passa allo stato BLOCKED. Il processo non pu essere
eseguito finch levento non occorrer, nel qual caso passerebbe allo stato READY.
2. se lo scheduler decide di assegnare la CPU ad un altro processo, passa allo stato
READY. Il processo pronto per essere eseguito nuovamente.

Le transizioni Blocked->Running e Ready->Blocked non hanno senso se si usa questo modello.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 18


Implementazione dei processi

Il sistema operativo tiene una tabella (process table) dove in ogni riga c un processo e tutti i
suoi attributi, compresi quelli che gli permettono di andare nuovamente in esecuzione (program
counter, stack pointer, ecc.) o che permettono al process scheduler di prendere decisioni
(priorit, ecc.).

Ora possiamo discutere di come


avviene il passaggio da un
processo all'altro: si effettua con gli
interrupts.

Quando viene inviato un interrupt,


come gi spiegato nel 1 capitolo,
vengono salvati nello stack I registri
del programma, poi viene trovato
l'indirizzo dell'interrupt handler
dall'area di memoria chiamata
interrupt vector ( una tabella).
Infine viene eseguito l'interrupt
handler e una volta terminato lo
scheduler decide il nuovo processo
da mandare in esecuzione.

Il cambio di processo in esecuzione oneroso, si devono salvare i registri, PC, stack pointer, la
cache, TLB, ecc.. Context Switch.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 19


Threads

Sono flussi di esecuzione allinterno di uno stesso processo che procedono in parallelo come
accade tra due processi diversi, ma a differenza loro condividono lo spazio di indirizzamento del
processo a cui appartengono (sono come processi all'interno di un processo che vengono
eseguiti in modo parallelo).
Pro:
1. I thread possono scambiarsi semplicemente informazioni tra loro
2. Sono molto veloci da creare (100 volte)
3. Velocizzano il programma se sono I/O Bound, poich solo il thread interessato si blocca
mentre gli altri possono continuare la loro esecuzione.
Immaginare come in un word processor il salvataggio del file interromperebbe tutto il
programma.

Il modello a thread
I thread sono quelle entit di un processo assegnate per far eseguire istruzioni dalla cpu. Di
conseguenza un thread ha bisogno di meno informazioni, ha bisogno delll'indirizzo del Program
Counter (PC), dello stack, etc... Se un processo ha pi thread, ogni thread ha il suo stack, il suo
PC, il suo stato, etc...

Tutorial ai thread POSIX: https://computing.llnl.gov/tutorials/pthreads/

Russo Alessio, Savaia Gianluca Sistemi Operativi - 20


Implementazione dei Threads
Un thread necessita di un proprio PC, stack, registri per poter essere eseguito.
Possiamo quindi vedere il processo come un entit che raggruppare risorse logicamente
correlate, mentre il thread un entit preposta allesecuzione.
Non prevista nessuna protezione tra thread (il programmatore se ne deve preoccupare)
poich sono pensati per collaborare a differenza dei processi che invece concorrono tra loro.
I Thread come i processi possono trovarsi in diversi stati.

La libreria Posix mette a disposizione diversi metodi per creare, distruggere un thread. Da
segnalare i metodi Pthread_yield e Pthread_join che permettono nellordine di rilasciare la CPU
in favore di un altro thread e di aspettare che il thread passato come paramentro termini.

User vs Kernel space


I thread definiti nello user space sono implementati tramite librerie apposite. Un run-time
system gestisce i thread di un processo. Il kernel (SO) non a conoscenza dellesistenza dei
thread.
Ogni processo ha la propria thread table (analogo della process table).

Quando un processo si blocca in attesa che un altro thread finisca il lavoro, chiama una
procedura del run-time system che decide quale altro thread mandare in esecuzione. Questo
avviene molto pi rapidamente comparato con una chiamata a sistema (non ho context switch).

C per un problema, una system call interrompe lintero processo. Perdo quindi il parallelismo
per il quale il thread nasce. Inoltre un thread non pu essere interrotto dal run-time system
poich non ci sono clock interrupts nello user space.

Poich i thread portano benefici solo quando sono I/O Bound(ci sono molte operazioni I/O), e
quindi quando effettuano molte chiamate a sistema, limplementazione nello user space non
efficace.
Laltro approccio prevede che sia il kernel a gestire i thread. Il kernel materr quindi una thread
table globale. Quindi per gestire i thread si utilizzeranno delle chiamate a sistema (p lente). Lo
scheduling effettuato tra tutti i thread non solo quelli di un processo. Per velocizzare i thread
possono essere disabilitati invece che distrutti, in modo da riattivarli se servono nuovamente.
Una chiamata a sistema in questo caso blocca solo il thread interessato.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 21


PRO USER:
1. Gestione molto veloce
2. Scheduling personalizzato
CONTRO USER:
1. Chiamate a sistema minano il parallelismo
2. se un thread non rilascia spontaneamente la CPU gli altri thread non verrano eseguiti

PRO KERNEL:
1. Gestione da parte del SO, Scheduling tra thread di diversi processi.
2. Chiamate a sistema bloccano solo il thread interessato
CONTRO KERNEL:
1. Gestione tramite chiamate a sistema (LENTE)

E' possibile pure avere un'implementazione ibrida, con alcuni thread nello user space e altri nel
kernel space: in pratica ogni kernel thread ha pi user thread al suo interno.

Scheduler Activations
E una soluzione ibrida. Il thread user space effettua una chiamata a sistema che ne blocca la
sua esecuzione. Il kernel ne prende atto e notifica (invece di bloccare) il run-time system con
una upcall (viene eseguita listruzione ad un indirizzo prefissato, tipo interrupt). Il run-time
system prende quindi il controllo e decide quale altro thread mandare in esecuzione.
Quando il vecchio thread sar in grado di riprendere lesecuzione, il kernel notificher
nuovamente con una upcall il run-time system.
E un approccio non usuale poich solitamente un livello pi basso (il O.S in kernel mode) non
notifica livelli pi alti.

Pop-up Threads
Esempio attesa di un messaggio.
Supponiamo che il thread in esecuzione aspetti l'arrivo di un messaggio, quindi deve essere
svegliato ( In blocked). Una volta svegliato avviene un context switch, prende il possesso
della CPU e fa il suo dovere.
Un pop-up thread invece viene creato nel momento in cui arriva il messaggio. Il vantaggio che
non ho bisogno di recuperare le sue vecchie informazioni (context switch) poich il thread
nuovo!
Nel 1 approccio il thread si blocca aspettando il messaggio.
Invece usando I Pop-Up threads il thread non si blocca e continua l'esecuzione, e verr creato
un nuovo thread (Pop-Up thread) all'arrivo del messaggio, occupandosene.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 22


Uso dei threads
I threads sono usati per permettere l'esecuzione in parallelo di pi parti di codice dello stesso
programma.
Principalmente ci sono 3 metodi in cui si pu fare un programma (o un mix di essi):
1. Singolo-thread
2. Pi threads
3. Macchina a stati finiti

Immaginiamo un server web a thread singolo: il server ascolta per messaggi in arrivo
invocando una system call => il processo si blocca finch non succede un evento. Una volta
arrivato un messaggio potr elaborarlo.
Invece implementandolo con pi threads si ha la seguente situazione: si ha un thread che
ascolta per messaggi in arrivo, ma solo questo thread si blocca. Una volta arrivato un
messaggio questo viene passato a un altro thread dormiente che viene svegliato =>
parallelismo.
Il terzo metodo di usare delle system call non bloccanti (insieme al polling a volte: il polling
un'operazione (non bloccante) che permette di conoscere lo status di una periferica). In pratica
succede questo: il server (thread singolo) fa uso di una read non bloccante per vedere se sono
arrivati messaggi, nel caso non siano arrivati continua con il proprio lavoro e poi ricontroller
dopo. Nel caso invece sia arrivato un messaggio, questo viene esaminato ed elaborato con
altre system call non bloccanti, la cui risposta verr segnalata tramite signal o interrupt. Ogni
volta che arriva una richiesta bisogna salvare la richiesta precedente e il suo status,
permettendo poi dopo al programma di poterci ritornare a lavorare. Questo sistema viene
chiamato MSF (macchina a stati finiti).

Threads Parallelismo, blocking system calls


Single thread Non c' parallelismo, blocking system calls
MSF Parallelismo, nonblocking system calls, interrupts o signals.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 23


Russo Alessio, Savaia Gianluca Sistemi Operativi - 24
Interprocess Communication

I processi hanno bisogno frequentemente di fare le seguenti cose:


1. Scrittura/lettura dati fra processi, passare informazioni senza far uso di signals o
interrupts.
2. Accedere a strutture dati/zone/risorse condivise
3. Eseguire operazioni nella sequenza corretta tenendo conto dello stato degli altri processi

Race Condition & Critical regions


Succede quando per esempio 2 processi vanno a modificare un'area di memoria condivisa.
Per evitarla bisogna garantire la mutua esclusione in quella parte di programma dove i due
processi vanno a toccare dati condivisi (critical region o critical section).

Bisogna evitare che i processi entrino


contemporaneamente nella regione
critica, senza che un processo che
non vi si trova dentro ne blocchi un
altro, o che debba aspettare un tempo
infinito per entrarvici.

Per risolvere il problema bisogna effettuare la mutua esclusione: ovvero se un processo gi


in quella regione critica, l'altro processo dovr aspettare che il primo processo abbia finito prima
di entrarci.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 25


Mutua esclusione - Busy waiting
Con busy waiting si intendono tutte quelle porzioni di codice dove c' un loop che esegue un
test e basta, consumando colpi di clock a eseguire questo test (o situazioni simili dove c'
consumo di cpu inutile).

Disabilitare gli interrupts


Il cambio di processo nella CPU avviene attraverso gli interrupts. Nel caso di una singola CPU
se si dovessero disabilitare tutti gli interrupts per un processo porterebbe a seri problemi se poi
questo non dovesse pi abilitarli. Bisogna notare che una cosa del genere si potrebbe fare solo
in kernel mode.

Variabili di lock
E' possibile avere una variabile condivisa in memoria, chiamata variabile di lock, inizialmente a
0. Quando un processo entra la sua regione critica questa variabile viene messa a 1, quando ci
esce viene messa a 0.. Se la variabile gi a 1 il processo aspetta (busy waiting) finch non
va a 0.
Sfortunatamente questa idea ha il problema che cerchiamo di risolvere, cosa succede se due
processi vedono contemporaneamente che la variabile a 0 e quindi entrano insieme nella
regione critica?

Russo Alessio, Savaia Gianluca Sistemi Operativi - 26


Alternanza stretta (strict alternation)

/* Processo A */

while(1)
{
while(turn != 0) ; /* loop Busy Waiting*/
critical_region();
turn = 1;
noncritical_regions();
}

/* Processo B */

while(1)
{
while(turn != 1) ; /* loop Busy Waiting*/
critical_region();
turn = 0;
noncritical_regions();
}

In questo caso si fa uso sempre di una variabile condivisa ma a causa dell'alternanza non ci
sono problemi.
Il problema che occorre nel ciclo che controlla la variabile: questo ciclo spreca tanti colpi di
clock e usa la CPU in maniera inutile. Inoltre questa soluzione prevede che I due processi si
alternino: potrebbero esserci problemi se un processo pi lento dell'altro.
Una variabile di lock usata in questo modo si chiama spin lock.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 27


Soluzione di Peterson

#define FALSE 0
#define TRUE 1
#define N 2

int turn, interested[N];

void enter_region(int process)


{
int other = 1-process;
interested[process] = TRUE;
turn = process;
while(turn == process && interested[other] == TRUE); /* LOOP, busy
waiting */
}

void leave_region(int process)


{
interested[process] = FALSE;
}

Questa soluzione prevede di implementare due funzioni, che devono essere chiamate
all'entrata e all'uscita di una regione critica: enter_region() e leave_region() nell'esempio.
Con due processi, A e B, nel caso A voglia entrare nella regione critica e B gi nella regione
critica, A dovr aspettare finch B non esce, causando busy waiting per via del loop while.
La soluzione di peterson pu essere estesa a pi processi.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 28


Istruzione TSL

enter_region:
TSL REGISTER, LOCK ; copia LOCK in REGISTER e mette LOCK a 1
CMP REGISTER, 0 ; if (REGISTER == 0)
JNE enter_region ; REGISTER == 0? se non lo ripeti il ciclo
RET ; entrato nella regione critica

leave_region:
MOVE LOCK, 0 ; mette LOCK a 0
RET

Questa soluzione prevede l'implementazione a livello hardware dell'istruzione TSL Test and
Set Lock.
Questa istruzione prende la variabile di lock che gli viene passata e la copia in un registro,
dopodich mette la variabile di lock a 1. Questa un'operazione atomica, ovvero viene fatta in
un solo colpo visto dall'utente e non ci sono rischi che qualcun'altro possa modificare la
variabile di lock nello stesso momento.
Realmente questo viene fatto attraverso il seguente meccanismo: viene bloccato il bus di
memoria finch non stata eseguita la TSL. Questo per far si che altri processori non possano
leggere o scrivere nello stesso momento.
Anche questo metodo presenta Busy Waiting, perch c' un test che viene ripetuto sul registro.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 29


Mutua esclusione - senza busy waiting
Il problema del busy waiting non solo consuma colpi di clock, c' anche il problema
dell'inversione di priorit.
Consideriamo due processi A e B, A con priorit alta e B con priorit bassa: ogni volta che A
nello stato ready passa subito in running, mentre B non pu essere eseguito quando A in
esecuzione.
Supponiamo che in un certo momento B sia nella sua regione critica ed A passa nello stato
ready => A passa nello stato running. Se ora A dovesse accedere alla regione critica che fa
conflitto con B dovrebbe fare busy waiting all'infinito perch B non otterr mai la sua chance di
uscire dalla regione critica perch A rimarr per sempre nello stato running.

Sleep & WakeUp


Una soluzione usare due primitive (funzioni) che bloccano invece di fare busy waiting.
Queste funzioni sono Sleep() e Wakeup().
La Sleep una system call che blocca il processo che la chiama ( lo mette nello stato blocked)
finch non viene svegliato da un altro processo con una Wakeup.
Wakeup una chiamata a funzione che ha un solo parametro, il processo da svegliare.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 30


Produttore consumatore
L'utilizzo di queste funzioni si pu vedere con il problema del produttore-consumatore: un
processo produce oggetti e il consumatore li usa.
#define N 100
int count = 0;

void producer()
{
int item;
while(1)
{
item = produce_item(); /* produci un oggetto */
if (count == N) sleep(); /* sleep se numero oggetti
prodotti=100*/
insert_item(item); /* inserisci oggetto */
count++;
if (count == 1) wakeup(consumer); /* count era a 0, sveglia
consumer*/
}
}

void consumer()
{
int item;
while(1)
{
if (count == 0) sleep(); /* dormo finch non mi svegliano */
item = remove_item(); /* c' un oggetto, lo tolgo */
count--;
if (count == N-1) wakeup(producer); /*sveglio producer se il
buffer era full */
consume_item(item); /* uso l'oggetto */
}
}

Anche questa soluzione ha per un problema:


Supponiamo che count sia 0. Il consumatore esegue if(count==0) ma non esegue la sleep
perch lo scheduler decide di far partire il produttore. Il produttore crea un oggetto e vedendo
che count diventato 1 invia un wakeup al consumatore. Ma il consumatore non ha eseguito la
sleep, quindi il wakeup viene perso.
Una soluzione usare il wakeup waiting bit. Quando viene effettuata una wakeup viene
settato questo bit a 1. Quando verr poi eseguita la Sleep questa controller se il bit a 1, se si
decrementa a 0 e continua l'esecuzione. Se invece fosse gi a 0 allora va in sleep e si blocca.
Se si hanno pi processi per servono pi bit.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 31


Semafori
Fa uso della Sleep, Wakeup e del wakeup waiting bits.
Si utilizza una variabile, chiamata semaforo, utilizzata per salvare I wakeup, ovvero svolge il
ruolo del wakeup waiting bits.
Inoltre al posto di avere la Sleep si ha la funzione down, per la wakeup invece si ha la funzione
up: da notare che queste sono funzioni atomiche.
La funzione up incrementa il valore del semaforo di 1, la funzione down decrementa il valore
del semaforo di 1, se a 0 va in Sleep.
Quando viene eseguita la funzione up viene scelto dal sistema un processo che in down con
quello stesso semaforo (ovvero che in blocked) e lo sveglia.
#define N 100
typedef unsigned int semaphore;

semaphore mutex = 1;
semaphore full = 0;
sempahore empty = N;

void producer()
{
int item;
while(1)
{
item = produce_item();
down(&empty);
down(&mutex);
insert_item(item);
up(&mutex);
up(&full);
}
}

void consumer()
{
int item;
while(1)
{
down(&full);
down(&mutex);
item = remove_time();
up(&mutex);
up(&empty);

Russo Alessio, Savaia Gianluca Sistemi Operativi - 32


consume_item(item);
}
}

Mutex
Quando non richiesta l'abilit di contare dei semafori si possono usare I mutex.
Un mutex una variabile che pu essere solo in due stati: locked e unlocked => ha bisogno di
1 solo bit o pi bits, dove 0 indica unlocked e tutti gli altri valori locked (contrario dei semafori).
Quando un processo o un thread hanno bisogno di accedere a una regione critica chiamano
mutex_lock(), mentre invece quando escono chiamano mutex_unlock(). Se pi thread o
processi sono bloccati sullo stesso mutex, uno di essi scelto a random gli viene concesso di
acquisire il lock. I mutex possono essere implementati in user space con l'istruzione TSL e con
il thread_yeld() (permette di evitare busy waiting passando nello stato blocked).
mutex_lock:
TSL REGISTER, mutex ; copia mutex in REGISTER e mette mutex a 1
CMP REGISTER, 0 ; if (REGISTER == 0)
BEQ ok ; se era 0, unlocked
CALL thread_yeld ; non era 0, vado in stato blocked
JMP mutex_lock ; riprovo
RET ; entrato nella regione critica

mutex_unlock:
MOVE mutex, 0 ; mette mutex a 0 => unlocked
RET

Inoltre nella libreria pthread oltre ai mutex ci sono delle funzioni speciali, pthread_cond_wait
e pthread_cond_signal, le quai offrono un ulteriore meccanismo di sincronizzazione chiamato
condition variables: infatti queste funzioni vengono chiamate se certe variabili soddisfano
certe condizioni.
La prima funzione blocca il thread in attesa di un segnale, la seconda invia un segnale al thread
bloccato e lo sveglia (simile al wakeup & sleep).

Link pthread: https://computing.llnl.gov/tutorials/pthreads/

Russo Alessio, Savaia Gianluca Sistemi Operativi - 33


Monitors
Si indica l'inizio e la fine della regione critica con Monitor e end monitor. Queste sono intere
zone dove consentito l'accesso solo a un thread per volta.
Ci sono meno errori perch lo fa il compilatore.

NB: I monitor non esistono in C come concetto di base.

Message passing
Fa utilizzo delle primitive send e receive per inviare e ricevere messaggi, cosi I processi
possono sincronizzarsi in base ai messaggi. Potrebbero sorgere vari problemi.
Sono primitive usate spesso per fare comunicazioni attraverso la rete (internet, lan, etc..:)

Barriers
Questo tipo di sincronizzazione si intende per gruppi di processi.
Alcune applicazioni sono divise in fasi o stages, e vogliamo che questi processi non possano
continuare alla prossima fase se non prima che tutti I processi di queli gruppo abbiano raggiunto
la fine della fase corrente: questo si pu implementare mettendo una barriera alla fine di ogni
fase, quando un processo raggiunge la barriera, questo va in stato blocked finch non abbiano
raggiunto la barriera tutti gli altri processi del gruppo.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 34


Russo Alessio, Savaia Gianluca Sistemi Operativi - 35
Scheduling

Lo scheduler quello che si occupa di decidere quale processo mandare in esecuzione.


La decisione dello scheduler fatta dall'algoritmo di scheduling.

Si dice CPU Bound process un processo che fa un uso intensivo della CPU e che fa poco uso
delle funzionalit di Input/Output che possono bloccare il processo.
Si dice I/O Bound process un processo che fa il contrario di quello CPU Bound.
Le CPU migliorano pi velocemente rispetto ai dischi e alle periferiche di input/output => I
processi diventano pi di tipo I/O Bound.
In generale meglio dare precedenza I processi I/O Bound: questo perch quando viene
eseguita un'operazione di Input/Output il processo si blocca e un altro processo viene eseguito,
e se tutti sono I/O Bound molti processi vengono eseguiti.

Quando fare lo schedule


In quattro situazioni lo scheduler deve prendere una decisione su quale processo mandare in
esecuzione:
1. Creazione di un processo
2. Terminazione di un processo
3. Quando un processo va nello stato blocekd
4. Quando accade un interrupt I/O
Esistono inoltre due metodi di scheduling:
1. Scheduling nonpreemptive (senza prerilascio): lascia eseguire un processo finch non
si blocca o lascia la cpu volontariamente
2. Scheduling preemptive (con prerilascio): lascia eseguire un processo per un tempo
fissato se non blocca o succede altro.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 36


Categorie algoritmi di scheduling in base al sistema
In generale un algoritmo di scheduling deve garantire:
Equa distribuzione del tempo di CPU
Tenere occupata buona parte del sistema sempre per evitare sprechi

Ci sono 3 tipi di sistemi dove in ognuno di questi ci sono diverse priorit da soddisfare:

1. Sistemi BATCH (sistemi usati nelle banche, etc...)


1. Throughput massimizzare il numero di lavori effettuati per ora
2. Turnaround time minimizzare il tempo fra l'imissione del lavoro e il suo
completamento
3. Tenere la cpu sempre occupata
2. Sistemi Interattivi (sistemi usati dagli utenti, personal computer, etc...)
1. Response time deve rispondere velocemente alle richieste dell'utente
2. Proportionality deve cercare di ottenere in termini di interattivit ci che l'utente si
aspetta
3. Sistemi Real time (sistemi che inviano dati di continuo, per esempio multimediali)
1. Meeting deadlines evitare di perdere dati
2. Predictability evitare la degradazione della qualit nei sistemi multimediali
3.
Throughput: numero di jobs(lavori) che il sistema completa per ora.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 37


Turnaround time: tempo che ci impiega un lavoro da quando stato inviato a quando
completato.
Response Time: tempo che si impiega da quando inviato il comando a quando si ottiene il
risultato.

NB: negli esercizi la schedulazione dei processi pu essere visualizzata facendo un grafico di
Gantt (http://it.wikipedia.org/wiki/Diagramma_di_Gantt ).

Sistemi Batch
Devono fare una grande quantit di lavoro. Massimizzare il throughput, garantendo che la CPU
venga utilizzata il massimo possibile.

FCFS First-Come First-Served


E' di tipo nonpreemptive. Viene eseguito il primo job che arriva. Penalizza I/O Bound.

SJF Shortest Job Firs


E' di tipo nonpreemptive. Bisogna sapere I tempi di completamento in anticipo. Serve a
minimizzare il tempo di turnaround. Ottimo se I processi arrivano nello stesso istante.

Shortest remaining time next


Questo un tipo di algoritmo preemptive. Pure in questo caso bisogna sapere I tempi di
completamento del job in anticipo. Quando arriva un nuovo job il suo tempo di completamento
comparato con quello attualmente in esecuzione: se prende minor tempo a finire, il nuovo job
passa in esecuzione e l'altro viene sospeso.
Ottimo per I jobs che durano poco.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 38


Sistemi interattivi
E importante la reattivit del sistema. Minimizzare il response time in base alle aspettative
dellutente.

Round-Robin
A ogni processo gli viene assegnato un quanto, ovvero un intervallo di tempo, durante il quale
ha il permesso di eseguire il programma. Se il processo ancora in esecuzione alla fine del
quanto, questo viene prerilasciato e il quanto dato a un altro processo. Se il processo si
blocca o finisce prima del quanto, quest'ultimo viene dato a un altro processo.
L'unico problema con questo algoritmo quanto deve essere grande il quanto di tempo,
perch passare da un processo all'altro genere overhead, ovvero bisogna fare operazioni che
non riguardando con l'obiettivo dell'algoritmo, quali: salvataggio dello stato del processo,
aggiornare tabelle, etc...si chiama process switch o context switch.
Quanto troppo piccolo implica tanto overhead, quanto troppo grande ne perdo in reattivit.
Non favorisce I processi I/O Bound.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 39


Priority scheduling
Il Round-Robin presuppone che tutti I processi abbiano la stessa priorit. Con questo algoritmo
invece ogni processo ha una priorit e ogni volta va in esecuzione il processo con priorit pi
alta.
Ci sono alcune cose da considerare:
Prevenire che I processi con priorit pi alta possano utilizzare la cpu per un tempo
indefinito
Si pu decrementare la priorit del processo a ogni colpo di clock(clock interrupt), se
la priorit diventa pi bassa di quella di un altor processo, il processo in esecuzione
viene prerilasciato e avviene un process switch.
Oppure si pu assegnare un quanto di tempo al processo. Finito il quanto tocca al
prossimo processo con priorit pi alta di passare nello stato running.
Assegnazione delle priorit: possono essere assegnata in modo statico o dinamico, in
base all'utente che ha lanciato il processo, se il processo passa da blocked a ready (gli
do priorit), etc...
Dare la CPU immediatamente ai processi I/O Bound, per permettergli di usare la CPU e
andare nello stato blocked per la prossima richiesta di I/O => favorire I processi I/O
Bound.

Code multiple
Si creano N priority class, ogni processo viene assegnato a una di queste classi di priorit. I
processi nella classe di priorit pi alta vengono eseguiti per un quanto di tempo. I processi
nella seconda classe di proprit pi alta vengono eseguiti per due quanti di tempo. I processi

Russo Alessio, Savaia Gianluca Sistemi Operativi - 40


nella classe di priorit ancora pi basas vengono eseguiti per quattro quanti di tempo, e cosi
via.
Una vota che un processo ha usato tutto il suo quanto di tempo, viene declassato di una classe
di priorit.
Reattivit e non penalizza troppo i CPU Bound.

SPN Shortest process next


Siccome il SJF migliora il response time (o turnaround time), si pu usare una cosa simile nei
sistemi interattivi.
Dato che non si pu sapere in anticipo quanto durer il comando di un processo, si pu cercare
di fare una stima, basandosi sulla durata delle esecuzioni precedenti.
T0
Sia il tempo stimato per un comando. Supponiamo che dopo l'esecuzione di quel

T1
comando duri secondi.

La nuova stima si fa facendo una media pesata dei due:


T est = pT 0 + ( 1 p ) T 1 , dove p mi

permette di fare la pesatura (solitamente p =0.5) .


Questa tecnica di stimare la durata basandosi sulla media pesata delle durate precedenti viene
chiamata a volte aging.

Sistemi real-time
I processi devono essere eseguiti categoricamente entro un certo limite di tempo (Deadlines).
Solitamente questo tipo di sistemi sono divisi in due tipi:
1. Hard real time :ci sono deadlines che devono essere rispettate assolutamente.
2. Soft real time: perdere una deadline non la fine del mondo.

In questo tipo di sistemi ogni programma diviso in un numero di processi, di cui ogni processo
conosciuto in anticipo il comportamento. Questi processi generalmente sono completati entro
1 secondo.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 41


Thread scheduling
In base al tipo di thread abbiamo:
User thread: tocca al scheduler dentro al processo di decidere cosa fare. Gli algoritmi
pi usati sono round robin e algoritmo a priorit Bisogna notare che fra thread dello
stesso processo non c' un full context-switch => pi veloce.

Kernel threads: lo scheduler decide fra tutti I threads di tutti I processi quale eseguire in
base all'algoritmo. Deve tenere da conto che pi veloce passare da un thread a un
altro thread dello stesso processo che fra thread di processi diversi, a causa del
context -switch.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 42


Russo Alessio, Savaia Gianluca Sistemi Operativi - 43
Problemi Classici dell'IPC

Produttore Consumatore con pthread


Il produttore riempe un buffer finch non pieno. Il consumatore toglie elementi finch ce n.
Il problema sta nel fatto che se il consumatore viene interrotto tra listruzione che controlla il
buffer (vuoto) e la wait, lui si perde il wakeup del produttore e si blocca tutto.

void produce()
{
int i = 0;
while(i++<MAX)
{
pthread_mutex_lock(&lock); /*accesso esclusivo a questarea*/
if(size==BUFF_SIZE)
pthread_cond_wait(&buff_controller, &lock); /*aspetto che il
buffer si svuoti*/
buffer[size++] = rand();
printf("P\titem inserted\t%d\tsize=%d\n", buffer[size-1], size);
if(size==1)
pthread_cond_signal(&buff_controller); /*sveglio il produttore*/
pthread_mutex_unlock(&lock);
}
pthread_exit(0);
}
void consume()
{
int i = 0;
while(i++<MAX)
{
pthread_mutex_lock(&lock);
if(size==0)
pthread_cond_wait(&buff_controller, &lock);
size--;
printf("C\titem removed\t%d\tsize=%d\n", buffer[size], size);
if(size==BUFF_SIZE-1)
pthread_cond_signal(&buff_controller);
pthread_mutex_unlock(&lock);
pthread_yield(); /*libero la CPU (consumo l'oggetto)*/
}
pthread_exit(0);
}

Russo Alessio, Savaia Gianluca Sistemi Operativi - 44


Filosofi a cena
5 filosofi seduti ad un tavolo circolare mangiano o pensano. Per mangiare necessitano di 2
forchette, ma sul tavolo ve ne sono solo 5. Lobbiettivo far si che tutti i filosofi mangino in un
tempo che non sia infinito.
Il problema che se ad esempio tutti i filosofi prendessero la forchetta alla loro destra, nessuno
potrebbe mangiare, nessuno sgancia le risorse aspettando laltra forchetta: Deadlock.
La situazione in cui un filosofo (processo) non ha mai accesso alle risorse viene chiamata
starvation.

Non soluzione del problema dei filosofi:


#define N 5 /* numero dei filosofi */

void philosopher(int i) /* filosofo numero...*/


{
while(1)
{
think(); /* pensa */
take_fork(i); /* prendo la forchetta a sinistra */
take_fork((i+1)%N); /* prendo la forchetta a destra */
eat(); /* mangia */
put_fork(i); /* posa la forchetta di sinistra */
put_fork((i+1)%N); /* posa la forchetta di destra */
}
}

Si pu risolvere utilizzando I monitor (java):

public syncrhonized void prendiForchette()


{
forchette[0].occupato = true;
forchette[1].occupato = true;
}

public syncrhonized void posaForchette()


{
forchette[0].occupato = false;
forchette[1].occupato = false;
notifyAll();
}

Russo Alessio, Savaia Gianluca Sistemi Operativi - 45


Soluzione usando I semafori in C:

#define N 5 /* numero dei filosofi */


#define LEFT (i+N-1)%N
#define RIGHT (i+1)%N
#define THINKING 0
#define HUNGRY 1
#define EATING 2

typedef int semaphore;


int state[N];
semaphore mutex=1;
semaphore s[N];

void philosopher(int i)
{
while(1)
{
think();
take_forks(i);
eat();
put_forks(i);
}
}

void take_forks(int i)
{
down(&mutex);
state[i] = HUNGRY;
test(i);
up(&mutex);
down(&s[i]); /* blocca se non sono state acquisite le forchette */
}

void put_forks(int i)
{
down(&mutex);
state[i] = THINKING;
test(LEFT);
test(RIGHT);
up(&mutex);
}

void test(int i)
{
if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT]!= EATING)
{
state[i] = EATING;
up(&s[i]);

Russo Alessio, Savaia Gianluca Sistemi Operativi - 46


}
}

Lettori e Scrittori
Database che ammette pi lettori in contemporanea, ma uno scrittore deve avere accesso
esclusivo. Quando uno scrive nessunaltro pu scrivere o leggere.
Il problema sta nel fatto che se permettiamo ai lettori di poter leggere sempre, lo scrittore non
avr mai accesso al database a meno che tutti i lettori non escano e non ne arrivino altri.

Presentiamo una soluzione in C che per ha il seguente problema: supponiamo che un lettore
stia leggendo il database, e ci sia uno scrittore in attesa. Nel frattempo arrivano altri lettori che
leggono il database (senza problemi), e cosi via finch lo scrittore non attende per un tempo
indeterminato. Per prevenire ci si pu inserire una coda FIFO, il primo che arriva fa qualcosa
(scrittore o lettore), per c' meno concorrenza => meno performance. Sono possibili altre
soluzioni.

typedef int semaphore;


semaphore mutex = 1;
semaphore db = 1;
int rc = 0; /* variabile usata per contare quanti lettori ci sono */

void reader()
{
while(1)
{
down(&mutex);
rc++;
if (rc == 1) down(&db); /* c' almeno un lettore, metti in lock db */
up(&mutex);
read_database();
down(&mutex);
rc--;
if (rc == 0) up(&db); /* non ci sono pi lettori, lascia db */
up(&mutex);
use_data();
}
}

void writer()
{
while(1)
{
think_up_data();
down(&db); /* se nessuno sta facendo niente su db, lock */
write_data_to_database();
up(&db);
}
}

Russo Alessio, Savaia Gianluca Sistemi Operativi - 47


Russo Alessio, Savaia Gianluca Sistemi Operativi - 48
Devo ancora correggerei compiti - mattina dell'orale, Baglietto

Russo Alessio, Savaia Gianluca Sistemi Operativi - 49


Memory Management
Il punto focale nella gestione della memoria la virtualizzazione. Con i processi abbiamo visto
che un ambiente multiprogrammato possibile, ma un programma per poter essere eseguito
deve stare in memoria!
Un altro concetto fondamentale quello di spazio di indirizzamento: ogni processo ha un suo
autonomo spazio di indirizzi, ovvero ogni processo pensa di possedere tutti gli indirizzi
disponibili per larchitettura che si sta usando (16, 32, 64 bit). Questo anche se la memoria fisica
disponibile inferiore!

In sistemi che non utilizzano virtualizzazione sulla memoria possibile avere un solo
programma alla volta in memoria, con conseguente context switch che prevede un swap out su
disco del vecchio processo e swap in di quello nuovo.
Un altro approccio (IBM) quello della static relocation. Il programma quando viene caricato in
memoria viene modificato in modo che le istruzioni che fanno riferimento a salti a indirizzi
assoluti vengano aggiornati in base a dove viene caricato il programma in memoria. Questo
approccio rallenta evidentemente loperazione di caricamento.

Base e Limit register


Un altro approccio quello di prevedere 2 registri, il base e limit register, nei quali sono
contenuti lindirizzo iniziale e finale di dove viene caricato il programma in memoria. Ad ogni
chiamata a funzione le istruzioni che utilizzano salti ad indirizzi assoluti vengono sommate con il
registro base, in modo da saltare al giusto indirizzo, e confrontate con il limit, per far si che non
escano dalla loro giurisdizione. Ad ogni context switch evidentemente il sistema operativo deve
aggiornare questi registri. Lo svantaggio che ad ogni istruzione di tipo salto si deve effettuare
una comparazione (non molto pesante) e una addizione (molto pesante).

Swapping
Non tutti i processi riescono a stare in memoria. Serve quindi un meccanismo che permetta di
spostare i processi da memoria a disco, e viceversa, in modo efficiente.

Allinizio la memoria immaginiamola vuota. Carico processi in memoria finch ho spazio per
farlo. Immaginiamo ora che un processo abbia terminato la sua esecuzione, lo dovr quindi
spostare su disco (swap out); si sar creato un buco in memoria che pu essere usato da
qualunque nuovo processo (basta che ci stia!).

Dopo svariati swap, la memoria sar piena di buchi ovvero si dice che sar frammentata. Per
evitare questo ogni tot secondi si potrebbe deframmentarla, ovvero compattarla, ma questa
operazione dispendiosa (circa 5sec per 1GB). Inoltre un programma non ha dimensione fissa,
potrebbe richiedere altra memoria dinamicamente; in questo caso il SO dovrebbe trovargli un
buco pi grande o togliere momentaneamente il processo dalla memoria.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 50


La gestione di questo tipo di memoria pu avvenire in 2 modi:
1. Bitmap, la memoria viene divisa in unit di allocazione (molto piccole) e il SO tiene una
tabella dove per ogni unit si ha un bit a zero se libera o ad 1 se occupata. Quando
un nuovo processo di dimensione k deve essere caricato, bisogna cercare nella tabella
una sequenza di k zeri consecutivi, operazione molto dispendiosa.
2. Linked List, ogni elemento della lista rappresenta o un processo o un buco. Ogni nodo
porta con s linformazione di dove inizia e quanto grande il segmento di memoria (il
processo o il buco). Quando un nuovo processo deve essere caricato diversi algoritmi
sono possibili: first fit, best fit (frammenta molto), worst fit, quick fit (liste di grandezza
predefinita), liste separate, ecc..

Memoria Virtuale
Il concetto alla base che i programmi non necessitano dellintero spazio di indirizzi per essere
eseguiti. Un primo approccio stato quindi quello degli overlays, ovvero il programmatore
doveva dividere il programma in diverse parti che potevano essere caricate ed eseguite
separatamente in memoria; questo per appesantiva il lavoro del programmatore e induceva a
errori. Il secondo approccio stato il paging.

Paging
Lo spazio di indirizzamento virtuale di un processo viene diviso in pagine di dimensione fissata
(4KB). Ogni pagina pu essere caricata in memoria, anchessa divisa in page frames della
stessa dimensione.

Il processo fa quindi uso di indirizzi virtuali: lindirizzo 1024 virtuale non corrisponde al 1024
fisico, bisogna sapere in quale page frame stata caricata la pagina corrispondente allinidirizzo
virtuale 1024! Si evince che deve esistere una tabella di corrispondenza pagina - page frame
per ogni processo.

La MMU (memory manegement unit) si occupa di tradurre gli indirizzi virtuali in indirizzi fisici e
inviarli sul bus (la memoria non a conoscenza della virtualizzazione). Non tutte le pagine
saranno caricate in memoria quindi si deve tenere conto, tramite un bit, della presenza o
assenza di una pagina in memoria. Quando viene chiamata una pagina che non presente in
memoria si parla di page fault, e sar il SO ad occuparsene.
Un indirizzo virtuale (16 bit) quindi diviso in 2 parti: numero di pagina virtuale (4 bit) e offset
interno alla pagina (12 bit). Loffset lo stesso poich pagine e page frames hanno la stessa
dimensione, mentre il numero di pagina viene fatto corrispondere al page frame in memoria
tramite la page table. In questo esempio ho uno spazio di indirizzamento di indirizzi diviso
in pagine di dimensione .

Russo Alessio, Savaia Gianluca Sistemi Operativi - 51


Russo Alessio, Savaia Gianluca Sistemi Operativi - 52
Page Table
Ogni processo ha la propria page table in memoria dove ogni riga corrisponde ad una pagina
del processo. Ogni riga contiene:
1. Page Frame Number, ovvero dov collocata la pagina in memoria
2. Present/Absent bit, che indica se la pagina in memoria o su disco
3. Protection bits, indicano se tale pagina pu essere scritta/letta/eseguita
4. Modified bit, indica se la pagina stata modificata ovvero se deve aggiornare il
contenuto su disco o meno (dirty bit)
5. Referenced bit, indica se la pagina stata indirizzata (utile nel page replacement)

Notare che se la tabella delle pagine fosse interamente in memoria ad ogni chiamata di
lettura/scrittura in memoria si trasforma in una ricerca nella page table dellindirizzo fisico.
Diversamente se fosse interamente in HW sarebbe immensamente costosa una tabella di
righe.

TLB
Il Translation Lookaside Buffer una cache completamente associativa (ovvero in una riga
vi scritto esplicitamente il numero di pagina e il numero di page frame) dove sono contenute
alcune (64) righe della tabella delle pagine. Funziona come una cache, ovvero tramite il
principio di localit nel tempo e nello spazio, dove una MISS comporta lo spostamento dalla
memoria al TLB della pagina richiesta. Si deve distinguere tra MISS e page fault!

La gestione del TLB pu essere fatta tramite HW, ovvero la MMU conosce dove sono
memorizzate le pagine in memoria e aggiorna il TLB in caso di MISS; oppure via SW dove il
sistema operativo ad occuparsi di aggiornare il TLB. Si scoperto che se il TLB abbastanza
grande non vi molta differenza in termini di prestazioni e inoltre nel secondo caso la MMU
molto pi semplice (risparmio spazio sul chip).

Paginazione a pi livelli
Si divide lindirizzo virtuale (32 bit) in 3 parti: PT1 (10), PT2 (10), OFFSET (12).
In memoria tengo quindi una tabella che viene indicizzata da PT1 ( righe) contenente il
numero della pagina di secondo livello da accedere (ho quindi 1024 pagine di secondo livello).
PT2 ( righe) viene usato per accedere alla riga della pagina di secondo livello che contiene il
page frame interessato.

Questo funziona perch un programma non utilizza mai tutto il proprio spazio di indirizzamento,
quindi molte pagine non saranno mai usate! In memoria tengo solo quelle tabelle contenenti le
pagine che utilizzer pi la PT1.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 53


Il problema di questo approccio che utilizzando unarchitettura a 64 bit, la PT1 risulterebbe
avere righe (circa 30 PB).

Inverted Page Table


La soluzione sta nellinvertire la corrispondenza. Se prima si faceva corrispondere ad ogni
pagina un page frame, ora si fa corrispondere ad ogni page frame una pagina di un processo.
La grandezza della tabella dipender quindi dalla dimensione della memoria.
Si perde in velocit nella traduzione poich pi processi hanno lo stesso numero di pagina.
Quindi si deve cercare la pagina x del processo y per ogni riga della tabella. Utilizzando il TLB
la ricerca avviene alla stessa velocit di prima, il problema arriva con la MISS. Per velocizzare
la ricerca possono essere organizzate tabelle indicizzate con hash.

Page replacement
Quando arriva un page fault devo decidere quale pagina togliere dalla memoria per far spazio a
quella mancante. Una volta scelta la pagina devo controllare il dirty bit poich se la pagina stata
modificata devo aggiornare il disco.

Algoritmo Ottimale
Non implementabile. Prevede che sia la pagina che verr usata pi avanti nel tempo ad essere
tolta. Sfortunatamente non si pu prevedere il futuro.

NRU
Prevede di scartare una pagina che non stata usata recentemente analizzando i bit R/M
ovvero guardando se la pagina stata indirizzata (R=1) e/o modificata (M=1). Ad ogni clock
interrupt il bit R viene azzerato cosi da tenere conto solo delle meno recenti.
Lalgoritmo semplice ma fa schifo.

FIFO
First in, First out.

Second Chance
E un FIFO migliorato, ovvero se la pagina che sta per uscire ha R=1 viene spostata in fondo e il
bit R viene azzerato.

Clock
E uguale a second chance ma implementato tramite una lista circolare dove il puntatore non
rimane sulla testa ma allultimo nodo analizzato, velocizzando lalgoritmo.

LRU
Least recently used prevede di utilizzare una lista ordinata dalla pagina utilizzata pi
recentemente a quella pi vecchia. Tenerla aggiornata troppo dispendioso.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 54


Una seconda implementazione prevede un contatore HW che conta ad ogni istruzione. Dopo un
indirizzamento il valore del contatore viene salvato nella page table. La pagina con il valore piu
basso quella da togliere.
Un terzo algoritmo quello della matrice: ogni riga/colonna indica una pagina; quando quella
pagina viene indirizzata si setta a 1 la riga e a 0 la colonna. La pagina con la riga con meno uni
quella da togliere.

NFU
Not frequently used, ogni pagina ha un contatore (SW) che viene incrementato in base al valore
del bit R. La pagina con il contatore pi basso stata letta meno volte. Il problema che se una
pagina stata usata molto 1 ora fa, ha il contatore alto e quindi non viene tolta anche se non
pi usata.

Aging
Come NFU solo che il bit R viene sommato nel bit pi significativo dopo aver shiftato il
contatore. In questo modo processi usati non di recente avranno i bit shiftati via e quindi un
valore del contatore pi basso.Ovviamente il difetto sta nel fatto che il possiamo registrare fino a
tot bit nel passato (8 bit a zero potrebbe significare che non viene usata da 8 tick oppure da 1
ora).

Working Set
Un nuovo approccio quello di capire quali pagine servono ad un processo per essere eseguite
in un certo intervallo di tempo. Quelle pagine fanno parte del working set (nota che il WS evolve
lentamente nel tempo). Ovviamente il WS deve rimanere preferibilmente in memoria, poich
togliere una pagina del WS implica avere molti page fault successivamente (thrashing).
Pagine che il processo utilizzava prima di essere swapped out fanno parte del WS e devono
essere caricate se il processo va di nuovo in esecuzione (prepaging).

Una implementazione efficiente quella di utlizzare il current virtual time invece che dei
contatori. Quando una pagina indirizzata viene aggiornato il suo R bit (azzerato ad ogni tick).
Ad ogni page fault il timer delle pagine con R=1 viene aggiornato al tempo corrente, invece le
pagine con R=0 vengono scartate se il loro timer ha un valore troppo indietro nel tempo
(confrontato con quello attuale) altrimenti non viene scartato ma non viene neanche aggiornato.

WSClock
Funziona come sopra, ma implementato come lalgoritmo di Clock. Una nota in pi che
quando trovo una pagina che non fa parte del WS controlla anche il dirty bit: se a uno non
posso rimuovere la pagina perch prima devo aggiornare quella su disco; quindi faccio partire
una scrittura e intanto cerco altre pagine che abbiano il dirty bit a zero (veloci da sostituire).

Note Generali
Locale vs Globale

Russo Alessio, Savaia Gianluca Sistemi Operativi - 55


La scelta della pagina da scartare pu essere fatta allinterno di un solo processo oppure tra
tutti i processi in memoria. Se Globale si pu scegliere un numero minimo di pagine che un
processo pu avere ed un numero massimo in modo che se il WS evolve si pu dare pi spazio
a quei processi che ne necessitano e meno a quelli con un WS ristretto. Se si usa quello locale
si dar uno spazio uguale a tutti i processi, il ch ha poco senso.
Esiste un algoritmo, il PFF (Page Fault Frequency), che determina lo spazio di allocazione da
destinare ad un processo in base al numero di page fault che produce.

Thrashing
Se un processo genera troppi page fault, il PFF prova a dargli pi spazio ma non ce n
abbastanza; quindi il SO pu decidere di spostarlo in memoria per un periodo di tempo.

Dimensione pagina
Pagine grandi implicano frammentazione interna. Pagine piccole implicano tabelle grandi.

Condivisione
Condividere il testo di un programma tra pi processi semplice se si divide lo spazio di
indirizzamento tra Dati e Istruzioni.
Condividere i dati deve avere delle accortezze in pi. La pagina marcata come READ ONLY,
cos se un processo prova a scrivere il SO interviene, divide le 2 parti e permette la scrittura
(copy on write).

Librerie condivise
Quando si compila un programma non vengono linkate le librerie ma una subroutine. Leffettivo
link avviene al runtime. Questo permette di avere solo un testo della libreria usato da pi
processi.

Mapped Files
I file vengono spostati da disco in memoria. Le scritture su file sono scritture su memoria non su
disco finch il file non viene chiuso. In questo modo si possono condividere i file tra processi.

Bloccare le pagine
Immagina un processo che permette ad un dispositivo I/O di riempire un buffer in memoria.
Quel processo viene bloccato (aspetta che il dispositivo finisca) e un altro processo genera un
page fault. La pagina contenente il buffer non deve essere rimossa per nessun motivo altrimenti
perdo i dati! Esiste un modo per bloccare le pagina in memoria (pinning).

Segmentazione
Si assegna uno spazio di indirizzamento personale ad ogni parte di un programma (anche uno
spazio per ogni procedura se si vuole). La paginazione quindi un segmento unico. Il vantaggio
che i segmenti possono avere dimensione non fissata, il che per porta a delle conseguenze.
Indirizzare una memoria a segmenti implica il fatto di avere nellindirizzo una parte che indichi
qual il segmento e loffset allinterno del segmento. Semplifica il linking e la condivisione.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 56


Russo Alessio, Savaia Gianluca Sistemi Operativi - 57
File System

Files
Un File sempre creato da un processo, ma a differenza della parte dati persiste in memoria
secondaria in modo da essere utilizzato pi avanti nel tempo (anche da altri processi).
Lestensione di un file pi una convenzione che una regola in UNIX. Windows invece associa
ad ogni estensione un programma per aprire il file.

Struttura
I file in UNIX e Windows sono semplicemente una sequenza di byte, sta al processo che li
utilizza dare un significato.
Un alternativa strutturare in record di n byte. In questo modo si semplifica la scrittura o lettura
in certi ambienti dove comodo poter leggere o scrivere degli interi record per volta.
In ultima analisi i dati potrebbero essere strutturati come un albero di record contententi una
chiave, utili nella ricerca e utilizzati in alcuni ambienti.

I file eseguibili invece hanno generalmente un formato particolare per essere riconosciuti:

Magic Number Numero che identifica il file come eseguibile

Text Size Dimensione del testo

Data Size Dimensione della parte dati

BSS Size Dimensione della parte dati inizializzati

Symbol Table Size Nomi delle variabili, utile nel debugging

Entry Point

Flags Attributi

Text Testo del programma

Data Dati

Relocation bits

Symbol Table Usata per il debug

Accesso

Russo Alessio, Savaia Gianluca Sistemi Operativi - 58


Si pu accedere ai file in 2 modi:
1. Sequenzialmente, ovvero scorro byte per byte
2. Random, posso saltare subito ad una certa posizione

Directories
Utilizzate per raggruppare insiemi di file. Esiste una gerarchia in modo che lutente sia facilitato
nel trovare il file che cerca.
Per inoltrarsi nelle directory si ha bisogno di path che indicano dove si trova un file. Esistono
due tipi di path:
1. assoluti, si inizia a elencare il percorso dalla cartella root
2. relativi, si inizia dalla working directory

Implementazione
Disco
I File System sono implementati su disco.
Il settore 0 di un disco viene chiamato Master Boot Record (MBR) e serve a far partire il SO.
La fine dellMBR contiene la tabella delle partizioni dove una in particolare denominata come
attiva. Il BIOS esegue il boot block (primo blocco della partizione attiva) che a sua volta carica
il SO in memoria.

File
I file sono divisi, un po come accade per i processi, in blocchi di dimensione fissata.
La regola : blocchi piccoli implica spreco di tempo, blocchi grandi spreco di spazio
(frammentazione interna).

Allocazione contigua
I file sono salvati in blocchi contigui. Se un blocco ha dimensione di 1KB, un file di 50 KB viene
memorizzato in 50 blocchi consecutivi.
Questo approccio semplice e molto performante (una volta trovato il blocco iniziale la testina
legge tutto il file in una volta).
Uno svantaggio pesante per la frammentazione.
Viene utilizzato nei CD-ROM, dove i file una volta scritti non possono essere pi cancellati.

Linked List
Il file rappresentato da una lista dove ogni nodo contiene linformazione della posizione del
blocco successivo. Non ho frammentazione ma una lettura impiega troppo tempo; inoltre utilizzo
una parte di blocco per il puntatore invece che per il dato stesso.
Un vantaggio che basta salvare nella directory lindirizzo del primo blocco per recuperare tutto
il file.

File Allocation Table

Russo Alessio, Savaia Gianluca Sistemi Operativi - 59


In memoria si tiene una tabella dove ogni riga corrisponde un blocco in memoria. In ogni riga vi
scritto qual il prossimo blocco da ricercare nella tabella stessa. La differenza con la lista qui
sopra che questa tabella in memoria primaria ed quindi molto pi veloce laccesso
(scorrerla). Per la dimensione di questa tabella cresce linearmente con la dimensione del disco
poich ogni blocco deve essere mappato.

I-nodes
Il file strutturato come un array (dimensione fissata) di indirizzi ai blocchi di memoria occupati
da quel file. Se un file ha bisogno di pi indirizzi, lultimo elemento della lista punter ad un
blocco in memoria dove saranno contenuti altri indirizzi.
Li-node deve essere caricato in memoria solo quando il file aperto quindi non occupa sempre
spazio. Ecco un esempio di accesso a file tramite i-node.

Directories
Devono contenere linformazione necessaria a localizzare il file su disco; il puntatore al primo
blocco occupato dal file sufficiente a questo scopo. Inoltre la directory deve contenere gli
attributi di tutti i file che contiene.

Blocchi liberi
Esistono due approcci:
1. Liste linkate
2. bitmap
Analizziamo il primo, immaginando di avere blocchi di 1KB e che per indirizzare un blocco in
memoria servano 32bit. La lista viene implementata usando come nodi i blocchi su disco, dove
ogni blocco contiene 1KB di indirizzi di blocchi liberi (1KB/32bit=255 indirizzi + indirizzo al nodo
successivo). In un disco da 500GB con blocchi da 1KB si hanno blocchi da mappare,

servono quindi blocchi (circa 2 milioni di blocchi corrispondenti a 2 GB) per


mappare tutta la memoria. Poich siamo su disco questo dato non ci preoccupa.

In memoria primaria si tiene solo 1 blocco della lista (occupo 1KB che corrisponde a conoscere
255KB di disco libero) in modo che se un processo crea un file sa dove memorizzarlo e pu
aggiornare velocemente la lista, analogamente se deve cancellarlo. Mantendendo in memoria
un blocco pieno a met si ha lefficienza ottimale. Tenendolo quasi pieno (di blocchi liberi) se un
file viene eliminato il blocco in memoria sar saturato e quindi dovr salvarlo in memoria e
crearne uno nuovo; se subito dopo un file viene creato dovr cedere dei blocchi quindi dovr
caricare dalla memoria il vecchio blocco. Come si evince c troppo I/O inutile che si pu evitare
tenendo il blocco pieno a met.

I bitmap invece memorizzano un bit per blocco e mappano tutta la memoria. Per mappare
500GB divisi in blocchi da 1KB ho bisogno di bit (circa 65MB), molto meno che nel
caso precedente anche se quando la memoria quasi piena la differenza minima.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 60


Consistenza
La consistenza del file system pu essere controllata da diversi tool offerti dal sistema operativo
stesso. Ci che questi programmi fanno controllare la lista dei blocchi liberi e lintero albero
delle directory contando quante volte un blocco occorre; diverse situazioni possono presentarsi:
1. Ogni blocco presente una sola volta in una delle due liste: tutto ok.
2. Un blocco non presente in entrambe le liste: nessun problema solo uno spreco di
memoria.
3. Un blocco presente in entrambe: deve essere tolto subito dalla lista dei blocchi liberi
altrimenti pu essere assegnato ad un altro file.
4. Un blocco presente 2 volte nella lista di quelli liberi: devo eliminare unoccorrenza
altrimenti pu venir assegnato 2 volte.
5. Un blocco presente 2 volte tra i file (2 diversi file): uno dei 2 file sar sicuramente
corrotto, non posso far altro che duplicare quel blocco in modo che ogni file abbia il suo
blocco.

Ottimizzazione
1. Cache per ridurre gli accessi al disco.
2. Block Read Ahead, leggo dei blocchi anche se non sono stati richiesti e li metto in
cache.
3. Ridurre i movimenti del braccio del disco cercando di collocare i blocchi di un file in
modo intelligente.
4. Deframmentare il disco in modo da riallocare i file in modo contiguo.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 61


Russo Alessio, Savaia Gianluca Sistemi Operativi - 62
Input/Output
Il S.O. si occupa di gestire le comunicazioni con i dispositivi di I/O.
La parte elettronica di ununit I/O il controller: nel controller ci sono dei registri usati per
comunicare con la CPU, per far ci ci sono 2 metodi:
1. A ogni control register assegnato un I/O port number => I/O Port space.
2. A ogni control register assegnato un indirizzo di memoria univoco non utilizzato (quindi
la ram non potr usarlo) => Memory mapped I/O:
3. Oppure implemetazione ibrida.
In pratica la CPU mette sul bus di sistema lindirizzo richiesto, e specifica se fa parte dellI/O
Port/Memory Space.
Ogni dispositivo di I/O attaccato a un computer ha bisogno di un codice che dipende dal
dispositivo per essere controllato: i device driver.

DMA (Direct memory access)


Con i metodi descritti la CPU a prendere i dati dallunit di I/O. Se la CPU busy e sta
facendo altro ci potrebbero essere dei ritardi. Un metodo alternativo il DMA. Il DMA un
componente elettronico che pu accedere al bus di sistema. Al posto di usare la CPU per
gestire la comunicazione con i dispositivi di I/O se ne occupa il DMA.
Quello che succede questo: la CPU inizialmente dice al DMA cosa e dove deve salvare i dati
(memoria primaria), poi da quel momento il DMA si occupa di gestire i dati delle unit I/O. Una
volta completato manda un interrupt alla CPU.

Gestione delle interruzioni


1. Quando un device pronto e deve fare qualcosa con la CPU, il device manda un
interrupt sul bus di sistema.
2. Questo segnale preso a carico dallInterrupt Controller, il quale vede se ci sono altri
interrupt in coda o pu direttamente inviare questo interrupt alla CPU.
3. La CPU quando riceve un interrupt smette di fare quello che stava facendo e comincia a
occuparsi di ci che ha causato linterrupt, prendendo il PC(program counter)
dallinterrupt vector (gestisce linterruzione).
4. Quando viene eseguita la gestione di un interrupt si salvano i registri assembler del
processo interrotto (nello stack del kernel), segnalato allInterrupt controller quando pu
inviare un nuovo interrupt, svegliato il driver opportuno, eseguito lo scheduler, e
caricato il nuovo processo.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 63


I/O Software
1. Device independence: Possibile scrivere programmi che possono accedere ai dispotivi
di I/O senza dover specificare il dispositivo in dettaglio qual .
2. Uniform naming: Il nome del file o del dispositivo non deve dipendere dal dispositivo
(deve essere una stringa o un intero).
3. Error handling: Se i livelli bassi non riescono a occuparsi dellerrore, allora devono
essere i livelli alti a occuparsene (livello software).
4. Synchronous (blocking) vs Asynchronous (interrupt-driven): Maggior parte dellI/O
asincrono (vedi il DMA, la cpu imposta il DMA e poi fa altro finch non gli arriva
linterrupt che ha finito il trasferimento). Per i software pi facile usare la modalit
sincrona: dopo una system call magari di lettura di un dispositivo, il programma
sospeso e rimane in attesa finch i dati non sono disponibili nel buffer. Tutto questo
gestito dal O.S.
5. Buffering: dove metto i dati una volta arrivati? In memoria o in un altro dispositivo?
6. Sharable vs dedicated devices: Alcuni dispositivi I/O possono essere utilizzati da pi
utenti allo stesso momento. Alcuni invece no. E in questo ultimo caso si hanno i
Deadlocks. (Per esempio i nastri magnetici non possono essere utilizzati da pi utenti
insieme, mentre i dischi di oggi si).

Programmed I/O
Se un processo vuole scrivere su un dispositivo di I/O in questa modalit esegue i seguenti
step:
1. System call per acquisire il dispositivo di I/O (error se non disponibile).
2. LO.S. copia i dati da scrivere su un buffer nel kernel space.
3. LOS. aspetta finch il dispositivo di I/O disponibile e scrive i dati. Se non riesce a finire
aspetta di nuovo finch il dispositivo non di nuovo disponibile: questa modalit si
chiama polling o busy waiting.
Semplice ma usa la CPU full time finch non ha finito.

Interrupt-driven I/O
E come il programmed I/O, ma la CPU al posto di aspettare il dispositivo fa altro, ovvero
esegue altri programmi (context switch). Una volta che il dispositivo di I/O ha finito invia un
interrupt.

DMA I/O
Lo svantaggio dellinterrupt-driven I/O che potrebbero esserci molti interrupt. Una soluzione
di usare il DMA che si occupa di gestire tutto quanto. Il DMA programmato come Programmed
I/O. Solo che il DMA controller a fare il lavoro e non la cpu. Svantaggi? E pi lento.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 64


Software layer
Il driver di un dispositivo la parte del sistema operativo che interagisce con il dispositivo:
1. legge/scrive i registri di controllo
2. tratta le caratteristiche a basso livello
3. fornisce una interfaccia astratta del dispositivo indipendente dai dettagli hw al resto del
sistema operativo
4. tipicamente sviluppato dal costruttore del dispositivo

Tipico funzionamento di un driver :


1. Inizializza il dispositivo
2. Accetta richieste di operazioni e ne controlla la correttezza
3. Gestisce le code delle richieste che non possono essere subito servite
4. Sceglie la prossima richiesta da servire e la traduce in una sequenza S di comandi a
basso livello da inviare al controllore
5. Trasmette i comandi in S al controllore eventualmente bloccandosi in attesa del
completamento dellesecuzione di un comando
6. Controlla lesito di ciascun comando gestendo eventuali errori 15
7. Invia lesito delloperazione ed eventuali dati al richiedente

Le interfacce astratte fornite dai driver vengono classificate in due categorie principali :
1. interfacce a blocchi (block-oriented) :
1. la lettura/scrittura sul dispositivo fisico avviene un blocco alla volta
2. tipicamente i dati scritti vengono bufferizzati nel SO finch non si raggiunge lampiezza
di un blocco ( es : dischi, nastri)
2. interfacce a caratteri (character-oriented) :
3. la lettura/scrittura sul dispositivo fisico avviene un carattere alla volta
4. Non c bufferizzazione (tastiera, mouse, )

Alcuni dispositivi forniscono entrambe le interfacce. Solitamente i driver implementano delle


funzioni standard di lettura scrittura (read(), write()).

Russo Alessio, Savaia Gianluca Sistemi Operativi - 65


Device indipendenti dal software I/O
Alcuni funzioni sono indipendenti dal dispositivo di I/O usato. Queste sono:
1. Interfaccia unificata per le funzionalit di sistema fornite ai driver
2. Buffering delle informazioni
3. Error and signaling
4. Allocare e rilasciare le risorse
5. Device independent block size

User space I/O software


Funzionalit del software di I/O che gira in spazio utente :
1. librerie linkabili da programmi utente (es. stdio, unistd ...)
passano i parametri alle System calls nel modo giusto
gestiscono la formattazione (es. printf()..)
2. spooling
processo utente (daemon)
directory di spool (in cui lutente copia il file da stampare, dopo il daemon si
occuper di stampare il file, lunico che pu farlo)

Russo Alessio, Savaia Gianluca Sistemi Operativi - 66


Dischi
I pi comuni sono i dischi magnetici (hard disks, floppy disks) e i dischi ottici (cd-rom, etc..)

Magnetic disks
I dischi magnetici sono organizzati in cilindri, di cui ogni cilindro contiene tante traccie quanti
sono i piatti del disco impilati uno sopra laltro. Le traccie sono divise in settori (Ogni settore
numerato attorno alla circonferenza del disco).

Nei vecchi dischi il numero di settori per traccia


era lo stesso per tutti i cilindri. I dischi moderni
hanno pi settori nella parte esterna del piatto
che in quella interna.
Nei dischi moderni supportato un sistema
chiamato Logical Block Addressing, il quale specifica che i settori sono identificati partendo da 0
in maniera crescente, senza preoccuparsi della geometria del disco.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 67


RAID - Redundant Array of Inexpensive Disks
Lidea nata per cercare di migliorare lI/O dei dischi, che a differenza della velocit delle cpu,
non ha la stessa crescita di velocit.
Lidea base di avere tanti dischi (non solo uno) per permettere il parallelismo. Questo fatto
grazie al Controllore Raid. Il controllore Raid fa si che al S.O. larray di dischi appaia come un
unico disco. I dati possono essere distribuiti su pi dischi per favorire le letture parallele di parti
dello stesso file, per far ci ci sono diverse tipologie di RAID (distribuire i dati su pi dischi si
chiama striping):

Raid Level 0 Il disco virtuale diviso in strips (striscie), dove ogni strip contiene settori. Lo strip 0 conterr
i settori da 0 a . Se lo strip corrisponde al settore. Nel livello 0 il RAID mette gli
strips consecutivi nei drive seguendo la strategia RoundRobin (Vedi immagine sotto).
Performance eccellente e di facile implementazione. Reliability molto bassa, non c ridondanza
=> non un vero RAID.

Raid Level 1 Come raid di livello 0 ma c una copia di backup di tutti i dischi. Quindi c ridondanza.

Raid Level 2 Il RAID level 2 non lavora con strips (strisce) di settori, ma lavora in base alla WORD, o
addirittura in base al byte. Per esempio: si supponga di lavorare su dati di 4 bit. Su questi 4 bit ci
aggiungiamo il codice di hamming => diventa di 7 bit. Poi ogni bit viene scritto su un drive
diverso in parallelo. Questo schema richiede che tutti i drive siano sincronizzati, inoltre vengono
eseguiti molti calcoli per il codice di hamming.

Raid Level 3 E simile al raid livello 2, ma al posto di usare un codice di hamming viene usato un bit di parit e
scritto nel parity drive.

Raid Level 4 Funziona come il raid di livello 0, ma ogni X strips viene calcolato il parity byte degli strips. Per
esempio se voglio fare il parity bytedi 4 strips e ogni strips grande Y bytes, far lo XOR di
questi strips, che risulter in un parity strip di lunghezza Y bytes.

Raid Level 5 Come livello 5 ma i parity byte sono sparsi fra i drive (vedi immagine).

Russo Alessio, Savaia Gianluca Sistemi Operativi - 68


Formattazione del Disco a basso livello
Struttura di un settore
Il settore di un disco fatto in questo modo (vedi limmagine).

Solitamente un settore contiene 512 byte di dati. La dimensione di questi campi dipende da chi
produce i dischi, anche se per esempio solitamente lECC ha 16 byte di dimensione.
Inoltre tutti gli hard disks hanno dei settori di riserva (spare sectors) da poter usare per
rimpiazzare i settori danneggiati.

Cylinder Skew
La posizione del settore 0 di ogni traccia spostata rispetto alla traccia precedente di un certo
numero di settori: questo numero si chiama offset, ed anche chiamato cylinder skew
(pendenza del cilindro). Questo fatto per permettere al braccio meccanico del disco di leggere
pi traccie in un unica operazione contigua sempre perdere dati. Ovvero: se leggo una traccia
intera, e ritorno al settore 0, se mi devo spostare alla traccia successiva nel frattempo che mi
sposto sono gi sul settore 0 della traccia successiva, per via di questo offset.

Supponiamo di avere un disco che ruota a


. Ogni traccia contiene 300 settori.
Quindi fa un settore ogni :

Se per far passare la testina da una traccia


allaltra servono allora durante questo
tempo passano settori. Quindi il
cylinder skew dovrebbe essere di 40 settori.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 69


Interleaving

Leggere senza interruzioni richiede un buffer dati di grandi dimensioni nel controller.
Consideriamo un controller che ha
un buffer di 512 byte, e ogni settore
ha dimensione dati 512 byte. Quindi
il buffer pu contenere al massimo
un settore per volta. Se volessimo
leggere due settori consecutivi, una
volta letto il primo dovremmo
calcolare lECC e trasferire i dati in
memoria, nel frattempo laltro settore
passato via. Questo pu essere
evitato intervallando i settori uno con
laltro come nellimmagine.

Formattazione del disco ad alto livello


Una volta completata la formattazione di basso livello, il disco viene diviso in partizioni (dischi
logici). Dove ogni partizione avr il suo File System (vedi la parte dei file system).

Russo Alessio, Savaia Gianluca Sistemi Operativi - 70


Algoritmi di scheduling per il braccio
Il tempo richiesto per leggere o scrivere un blocco dal disco determinato da 3 fattori:
1. Seek time (tempo di ricerca, ovvero il tempo che impiega il braccio a spostarci al cilindro
giusto), solitamente il valore pi alto e pi decisivo => ridurlo migliora le performance
di molto
2. Rotational delay (tempo che serve per far passare il settore cercato sotto la testina del
braccio)
3. Tempo di trasferimento dati

Vediamo degli algoritmi per migliorare il seek time:

1. FCFS (First-Come, First-Served): ogni richiesta inviata al braccio meccanico viene


messa in una coda FIFO ed eseguita una alla volta. Non si pu far molto per ottimizzare.
2. SSF (Shortest Seek First): basato sul FCFS, basato sul fatto di trovare la richiesta
che richiede il minor tempo di seek time (di spostamento della testina). Se mi arrivano
richieste per i cilindri 1, 7, 13, 18 e sono nel cilindro 12, prima leggo nel cilindro 13, poi
nel 18, infine nel 7 e 1. Rispetto al FCFS dimezza i movimenti del braccio. Il problema
del SSF che se continuano ad arrivare richieste per cilindri con numero alto, e il
braccio gi li, il braccio rimarr sempre la senza andare ai numeri bassi a causa della
congestione di richieste.

3. Elevator algorithm ( algoritmo dellascensore): Questo algoritmo cerca di risolvere il


problema del SSF. Si tiene un bit che indica la direzione del braccio, UP o DOWN. Se
up il braccio si sposta verso i cilindri alti, senno il contrario. Se non ci sono richieste
pendenti nelle posizioni seguenti, il verso di direzione del braccio si inverte. Una
modifica a questo algoritmo che una volta arrivati alla richiesta di posizione pi alta,
si riparte dal fondo, ovvero dalla richiesta con posizione pi bassa e si va verso lalto.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 71


Gestione degli errori
Nei settori danneggiati ci sono generalmente due approcci:
1. Occuparsene con il controller del disco
2. Lasciare che sia il O.S. a occuparsene

Nel primo approccio, prima che il disco sia venduto, testato ed scritta una lista dei settori
danneggiati. Per ogni settore danneggiato c un settore di riserva che lo sostituisce
(naturalmente non sono infiniti).
Ci sono due modi per fare questa sostituzione:
1. A livello logico segnare che uno dei settori di riserva il settore danneggiato, ovver
mappare (vedi immagine b) )-
2. Shifto tutti i settori avanti di uno (vedi immagine c) ).

Russo Alessio, Savaia Gianluca Sistemi Operativi - 72


Stable storage
Idealmente un disco dovrebbe funzionare sempre senza errori. Ci non possibile. Quello che
si pu fare il seguente: quando una fatta una richiesta di scrittura, il disco o scrive
correttamene i dati, o non fa niente, lasciando i dati esistenti intatti. Questo sistema chiamato
Stable Storage ed implementato a livello software: lobiettivo di mantenere il disco
consistente. Nello stable storage, ogni volta che eseguiamo una scrittura si garantisce che il
valore scritto corretto oppure uguale a quello vecchio: permette di mettersi al riparo da errori
che si verificano durante una scrittura.

Lo stable storage funziona con due dischi identici, con i blocchi corrispondenti che lavorano
insieme. In assenza di errori, i blocchi corrispondenti su entrambi i dischi sono identici ( i dati
sono identici) .
Sono definite 3 operazioni:

1. Stable writes: Scrivo un blocco sul primo disco, poi verifico se stato scritto
correttamente. Se non lo stato, allora riprovo finch non va a buon fine. Dopo
fallimenti, il blocco sostituito con uno di riserva, e loperazione ripetuta. Una volta
scritto correttamente sul 1 disco, rifaccio la stessa operazione sul secondo.
2. Stable reads: Prima legge un blocco dal disco 1. Se il checksum calcolato non
combacia con lECC, viene riletto fino a volte. Se dopo volte non ancora stato
letto, allora viene letto dal 2 disco.
3. Crash recovery: Dopo un crash, un programma di recovery confronta entrambi i dischi
per vedere le differenze fra i blocchi. Se due blocchi sono uguali, non fatto niente. Se
uno di questi ha un ECC incorretto, il blocco corrotto sovrascritto con i dati buoni. Se
entrambi i blocchi hanno ECC giusto, ma dati differenti, il blocco dal 1 disco scritto nel
corrispondente blocco del 2 disco.

Russo Alessio, Savaia Gianluca Sistemi Operativi - 73


Russo Alessio, Savaia Gianluca Sistemi Operativi - 74
Deadlocks

Russo Alessio, Savaia Gianluca Sistemi Operativi - 75


Un insieme di processi in stallo se ogni processo nellinsieme sta aspettando un evento che
solo un altro processo nello stesso insieme pu generare.

1. Generalmente levento il rilascio di una risorsa (stampanti, nastri, tabelle, )


2. Nessuno dei processi in stallo pu
1. Passare in esecuzione
2. Rilasciare risorse
3. Essere riattivato

Supponiamo che un processo occupi la risorsa A e che richieda la risorsa B: se allo stesso
istante un altro processo occupa B e richiede A => entrambi i processi restano bloccati (vanno
in deadlock).

Risorse
Ci sono due tipi di risorse:
1. Un dispositivo hardware (stampante)
2. Uninformazione (es: un record di un database)

Una risorsa qualcosa che deve essere acquisita, usata, e rilasciata nel corso del tempo.

Se una risorsa non disponibile quando richiesta, il processo che la richiede forzato ad
aspettare. In alcuni O.S. il processo bloccato automaticamente quando una richiesta fallisce,
e risvegliato quando la risorsa disponibile.
In altri invece la richiesta fallisce e basta con un codice di errore.

Risorse prerilasciabili - non prerilasciabili


Una risorsa pu avere due caratteristiche:
1. Prerilasciabile:: un tipo di risorsa che pu essere presa dal processo che la sta
usando senza nessun effetto negativo (es: la memoria centrale).
2. Non prerilasciabile: un tipo di risorsa che non pu essere presa dal processo che la
sta usando senza effetti negativi (es: CD-ROM).

Acquisizione della risorsa


Per alcuni tipi di risorse (es: i records di un database), tocca al processo utente di gestire le
risorse. Un modo di assegnare un semaforo a ogni risorsa, inizializzato a 1 (anche i mutex
vanno bene).

Nellimmagine a destra si vede un esempio di come vengono gestite 2 risorse con un solo
processo.
Le cose si complicano se ci sono pi processi.

Nel caso di due processi, la figura (a) mostra


un codice senza stalli. La figura (b) invece
mostra un codice con un potenziale stallo:
ovvero il caso che il processo A prenda la
risorsa 1 e contemporaneamente il processo
B prenda la risorsa 2, dopodich i due
processi aspetteranno allinfinito.

Resource deadlocks - Condizioni per lo stallo


E stato dimostrato che per esserci uno stallo di risorse si devono presentare le seguenti
condizioni:

1. Mutua esclusione: ogni risorsa o assegnata a un solo processo o libera.


2. Hold & wait: processi che hanno acquisito delle risorse e che le stanno utilizzando
possono richiedere nuove risorse.
3. Risorse non prerilasciabili: risorse acquisite da un processo non possono essere
rilasciate in anticipo senza che sia il processo stesso a rilasciarle.
4. Attesa circolare: ogni processo aspetta una risorsa occupata da un altro processo nella
catena circolare.

Modelli per lo stallo


Grazie ai grafi orientati possiamo modellare come vengono allocate le risorse in modo visivo.
Indichiamo i processi con dei cerchi, e le risorse come dei quadrati:
1. Una freccia che va dalla risorsa al processo indica che la risorsa stata acquisita dal
processo in questione.
2. Una freccia che va dal processo alla risorsa indica che il processo ha fatto richiesta per
la risorsa.

Si chiamano anche grafi di Holt.

In generale ci sono quattro strategia usate


negli stalli:
1. Ignorare lo stallo.
2. Riconoscere ed eliminare lo stallo:: aspettare che accada lo stallo e poi agire.
3. Evitare(impedire) lo stallo con una scrupolosa allocazione delle risorse.
4. Prevenire lo stallo negando una delle quattro condizione richieste.

Ignorare lo stallo - Algoritmo dello struzzo


Questo algoritmo semplicemente ignora lo stallo, fa finta che il problema non esista. E una
soluzione ragionevole se lo stallo accade raramente, ed il costo per evitare lo stallo troppo
elevato.

Solitamente Unix e Windows usano questo metodo.


Riconoscere lo stallo ed eliminarlo
Con questa tecnica, il sistema non cerca di prevenire gli stalli dallavvenire: invece li lascia
avvenire e dopodich prova a risolverli.

Riconoscere lo stallo con una sola risorsa di ogni tipo


Per vedere se esiste uno stallo, nel caso ci sia solo una risorsa di ogni tipo, basta fare un
grafico delle risorse (un grado di Holt). Se esiste un ciclo nel grafico allora c uno stallo. Da
questo grafo possiamo pure vedere quali sono i processi coinvolti.

Riconoscere lo stallo con pi risorse di ogni tipo


Nel caso ci siano pi risorse di ogni tipo, potrebbe risultare difficile usare dei grafi. In questo
caso si usa la notazione matriciale.
Indichiamo con:
1. il vettore delle risorse esistenti, dove ogni componente del vettore
indica il numero massimo di risorse di quel tipo (Per esempio potrebbe essere il
numero di scanners, il numero di CD-ROM, etc...).

2. il vettore delle risorse disponibili, dove ogni componente del


vettore indica il numere di risorse disponibili di quel tipo.
3. il vettore delle risorse possedute, equivale a
4. , matrice con righe (una riga per processo), e colonne (una per ogni
tipo di risorsa). Questa matrice contiene il numero di risorse allocate tipo di risorsa da
ogni processo.
5. , matrice con righe (una riga per processo), e colonne (una per ogni
tipo di risorsa). Questa matrice contiene il numero di risorse richieste per tipo di risorsa
da ogni processo.
Ne segue che (Il numero massimo di risorse esistenti del tipo uguale
al numero di risorse disponibili di tipo pi il numero di risorse di tipo usate dai processi).

Per controllare se ci sono stalli bisogna procedere cos:


1. Controllo se ci sono richieste che possono essere soddisfato (controllo la matrice ,
comparando ogni riga con il vettore ). Se non si pu soddisfare nessuna richiesta il
sistema in stallo.
2. Nel caso non ci siano stalli, do le risorse a un processo cui posso soddisfare la richiesta
di risorse. Dopodich scrivo il nuovo vettore aggiungendo le risorse rilasciate dal
processo in questione.

Nellimmagine si vede che si pu


soddisfare la richiesta per il processo
della 3 riga.
Una volta fatto il vettore A sar il

seguente: .
Dopodich cancello la 3 riga dalle
due matrici e vedo che posso
soddisfare la richiesta del processo

nella 2 riga .
Ora posso soddisfare anche la
richiesta del 1 processo Non ci
sono stalli.

Eliminare uno stallo


Una volta riconosciuto che c uno stallo bisogna eliminarlo e ripristinare il sistema.

Eliminare lo stallo tramite prerilascio


Si forza il rilascio di una risorsa da parte di un processo, per poter dare la risorsa a un altro
processo e poi ridarla al processo originale.

Eliminare lo stallo tramite rollback


Si esegue un salvataggio periodico dello stato dei processi, in caso di stallo si ripristina lo stato
del processo che ha causato lo stallo ( ovvero detiene una risorsa che richiesta da altri
processi) a uno stato in cui non aveva acquisito questa risorsa.
Eliminare lo stallo tramite terminazione dei processi
E il metodo pi semplice. Si forza la terminazione di uno dei processi nel ciclo di stallo. Se
possibile si sceglie un processo che pu essere fatto ripartire.

Evitare lo stallo
Solitamente non si pu far uso dei metodi presentati precedentemente per riconoscere uno
stallo, perch viene fatta una richiesta alla volta, e non tutte insieme.
Si possono comunque evitare gli stalli se ci sono date certe informazioni dallinizio. Purtroppo
sar quasi impossibile implementare in un sistema degli algoritmi per evitare lo stallo.

Traiettorie delle risorse


I principali algoritmi usati per evitare gli stalli si basano sul concetto di stati sicuri. Il sistema
operativo quando deve assegnare una risorsa vede se assegnandola ci potrebbe essere uno
stallo.

In questa immagine le linee oblique che


vanno in un solo verso indicano che il
processo A o B sono in attesa di una
risorsa. Nel centro si
hanno linee oblique in entrambe le
direzioni. Se si arrivasse in quello stato
si avrebbe uno stallo perch tutti e due i
processi aspetterebbero per una risorsa
che ha laltro processo.
Stati sicuri e insicuri
Si basa sullutilizzo dei vettori e delle matrici . Uno stato detto sicuro se esiste
almeno un ordine in cui le richieste di risorse dei processi possano venir eseguite senza finire in
uno stallo, anche se richiedono il numero massimo di risorse che potrebbero servirgli.

Facciamo lesempio con un solo tipo di risorsa: supponiamo ci siano al massimo 10 elementi di
questa risorsa.

Partiamo dalla figura (a): il processo A ha 3 elementi di questa risorsa, B ne ha 2 e C ne ha altre


2. Dato che ci sono al massimo 10 elementi significa che ne rimangono 3 liberi. Ad A potrebbero
servire fino a 9 elementi di questa risorsa, B fino a 4, C fino a 7. Ci significa che con 3 elementi
liberi posso soddisfare B dato che gliene bastano solo 2 per arrivare a 4 (b). Una volta fatto ci
ho 5 elementi liberi Posso soddisfare il processo C (c) . Infine ho 7 elementi liberi, quindi
posso soddisfare A Non sono avvenuti stalli, e uno stato sicuro.

A lato c la
dimostrazione di
uno stato non
sicuro.

Bisogna notare che uno stato non sicuro non andr sicuramente in stallo. Questo perch sono
decisioni prese sul numero massimo di risorse che ogni processo potrebbe chiedere, in realt
un processo ne richieder molte meno di risorse. Quello che possiamo dire che se uno stato
sicuro, allora garantito che non ci saranno stalli.
Algoritmo del banchiere per una risorsa
Prima di iniziare lesecuzione ogni processo dichiara il massimo numero di risorse che gli sono
necessarie. Ad ogni richiesta di una nuova risorsa lalgoritmo controlla se accogliere la richiesta
porta ad uno stato sicuro o insicuro:

1. Per ogni processo si calcolano le unit di risorsa ancora richiedibili (R = Max - Has)
2. Si considerano i processi in ordine di R crescente controllando che ognuno possa
ancora richiedere R risorse e terminare correttamente
3. Se tutti i processi possono terminare correttamente lo stato sicuro
4. Solo le richieste che portano a stati sicuri sono accolte.

Algoritmo del banchiere per risorse


multiple
Funziona esattamente nello stesso modo del riconoscere
lo stallo con pi risorse per tipo. Solamente che la matrice
si riferisce al numero massimo di richieste che potrebbe
fare il processo. Purtroppo raramente usato perch non
si conoscono a priori quante risorse potrebbe usare al
massimo un processo. Naturalmente si applica anche al
caso con una sola risorsa.
Prevenire lo stallo
Per prevenire lo stallo bisogna eliminare almeno una delle quattro condizioni necessarie per
avere uno stallo.

Eliminare la condizione di muta esclusione


Alcuni dispositivi (ad esempio le stampanti) possono essere gestiti con spool:
1. I processi scrivono loutput in unarea di spool
2. Solo il gestore della stampante richiede e usa la stampante
3. Quindi lo stallo per la stampante eliminabile
Purtroppo per:
1. Non tutti i dispositivi possono essere gestiti con spool
2. Ci pu essere stallo nellaccesso allarea di spool

Bisogna evitare di assegnare una risorsa quando non strettamente necessario, e far in modo
che il minor numero possibile di processi possa richiedere una risorsa.

Eliminare la condizione di Hold & Wait


Bisogna prevenire che i processi che hanno gi acquisito delle risorse ne possano acquisire
delle altre.
Per far ci bisogna richiedere che tutti i processi facciano domanda per tutte le risorse che
useranno prima che vengano eseguiti. Se ogni risorsa disponibile il processo verr eseguito e
le risorse allocate.

Un problema che i processi non conoscono a priori quante risorse useranno. Infatti se lo
sapessero si potrebbe usare lalgoritmo del banchiere. Un altro problema che con questo
metodo le risorse non sarebbero utilizzate in modo ottimale.

Una variazione di richiedere al processo che ha fatto domanda per una risorsa di rilasciare
temporaneamente tutte le risorse che ha gi acquisito, e quindi richiedere tutte quelle
necessarie.

Eliminare la condizione non prerilasciabilit


Questa opzione non possibile: dipende anche dal tipo di risorsa (con la memoria si pu fare,
con una stampante no: non posso cedere la stampante a un altro processo a met della
stampa!). Si pu fare virtualizzando la risorsa (spooling per esempio). Non tutte le risorse si
possono virtualizzare.
Eliminare la condizione di attesa circolare
La condizione di attesa circolare pu essere eliminata in vari modi:
1. Un modo di implementare una semplice regola: un processo legato solo e
solamente a una sola risorsa in qualsiasi momento. Nel caso gli servisse una
seconda risorsa deve prima rilasciare la prima.
2. Un altro metodo di ordinare le risorse in modo numerico.
La regola la seguente: un processo pu richiedere le risorse quando vuole, ma le
richieste devono essere fatte in modo numerico crescente. Se un processo ha acquisito
una risorsa di tipo 1 poi potr chiedere risorse di tipo > 1.
Nel caso il processo abbia acquisito una risorsa di tipo 3, non potr acquisire risorse di
tipo < 3.
Una piccola variante la seguente: se un processo rilascia tutte le risorse poi potr
ripartire dalla risorsa di numero pi basso.

Tabella riassuntiva delle condizioni


Esercizi
Esercizio 1
Esercizio 2
Esercizio 3
Esercizio 4

Si consideri la seguente soluzione SBAGLIATA al problema dei filosofi a cena riportata dal
testo:

#define N 5
void philosopher (int i) {
while (TRUE) {
think();
takeForks(i);
takeForks((i+1)%N);
eat();
putForks(i);
putForks((i+1)%N);
}
}

Spiegare perch la soluzione proposta sbagliata, e mostrare un esempio di esecuzione in


cui la soluzione non funziona.

Le forchette sono risorse condivise dai diversi processi (filosofi) e bisogna quindi garantirne
laccesso mutuamente esclusivo! La soluzione corretta prevederebbe quindi un mutex che
garantisce accesso esclusivo alla regione takeForks e PutForks.

I problemi che si possono verificare sono molteplici. Per esempio tutti i processi possono
venire interrotti dopo la prima takeForks e quindi ogni filosofo ha una forchetta in mano:
nessuno potr pi mangiare. Altre problematiche potrebbero verificarsi allinterno del
metodo takeForks stesso.
Esercizio 5

In un sistema di gestione della memoria con swapping dinamico, che utilizza un registro
base B ed un registro limite L
per assicurare la protezione degli spazi di indirizzamento, abbiamo 32M di RAM, di cui gli
8M di indirizzi bassi occupati dal Sistema
Operativo. Consideriamo i seguenti programmi : A, di 4M, B di 16M, C di 12M e D di 6M.
Supponiamo inoltre che i programmi passino in esecuzione nellordine seguente: 1) A, 2) B,
3) C, 4) D, 5) A .
Ogni volta che un nuovo processo passa in esecuzione discutere :
a) la mappa della memoria centrale
b) che cosa contengono i registri base e limite.
I registri BASE e LIMIT contengono lindirizzo iniziale e finale in memoria del processo in
esecuzione. Immaginiamo che in memoria sia solo presente il sistema operativo.

FREE
FREE FREE FREE FREE
FREE
B
A C D D
OS
A
OS A C C
OS
OS A A

OS OS

BASE: 8M BASE: BASE: 24M BASE: 8M


BASE: 12M
LIMIT: 12M 12M LIMIT: LIMIT: 24M LIMIT: 30M LIMIT: 12M
28M

NB:

1. M = 1024, quindi 8M si riferisce allindirizzo 8*1024=8192.


2. OS caricato dallindirizzo 0 allindirizzo 8191.
3. B viene spostato su disco (SWAP OUT) poich non vi spazio sufficiente per caricare C. Spostare A non
avrebbe portato spazio sufficiente neanche dopo una deframmentazione della memoria.

4. Quando A viene eseguito nuovamente non ha bisogno di essere caricato in memoria e i registri BASE e
LIMIT corrispondono a quelli della sua prima esecuzione (poich non stato spostato).
Esercizio 6

Un disco ha un tempo di seek di 0,5ms per ogni cilindro attraversato, un tempo di rotazione
di 6ms e un tempo di trasferimento dei dati di un settore di 12 microsecondi. Inoltre si fanno
le seguenti ipotesi :
- la testina attualmente posizionata sul cilindro 13
- l'attesa media prima che il settore desiderato passi sotto la testina dopo il seek di mezza
rotazione
Supponendo che al tempo attuale arrivino contemporaneamente le seguenti richieste di
lettura di settori (un settore per cilindro):
a- cilindro 14
b- cilindro 11
c- cilindro 19
d- cilindro 2
e- cilindro 31
Calcolare il tempo di completamento delle richieste nel caso in cui venga utilizzato
l'algoritmo SSF e l'algoritmo dell'ascensore (con direzione iniziale verso l'alto).

SSF (Shortest Seek First) sposta la testina del disco sul cilindro pi vicino alla sua posizione
attuale: 13 14 11 19 31 2.
Lalgoritmo dellascensore invece sposta la testina verso lalto finch ho un settore da leggere in
quella direzione, poi la cambia: 13 14 19 31 11 2.

Per calcolare il tempo di completamento si deve tener conto del fatto che per attraversare ogni
cilindro si impiegano 0,5 ms e che per trovare il settore si impiegano 3 ms; quindi per leggere il
settore nel cilindro 11 (partendo dal cilindro 14) si impiegano (0,5*3)ms + 3ms.

Infine bisogna ipotizzare la dimensione del buffer del controllore del disco:
Buffer = 1 settore: bisogna aspettare che ogni volta letto un settore la CPU lo legga dal
buffer per far spazio al successivo. Bisogna quindi aggiungere ad ogni passaggio 12us
di attesa, ovvero (12*5)us di attesa totale.
Buffer > 5 settori: il trasferimento dei dati avviene solo al completamento di tutte le
letture, quindi bisogna aggiungere una sola volta (alla fine) i 12us.
Esercizio 7

Dati i seguenti jobs:

Tempo cpu
A 25 ms
B 50 ms
C 10 ms
D 30ms

Calcolare il throughput e il tournaround medio per SJF e RR. Il tempo di commutazione 1


ms e nel round robin il quanto di tempo 10 ms.

SJF (Short Job First) esegue i processi in ordine crescente di tempo di CPU e vengono eseguiti
fino a che non hanno terminato lesecuzione stessa: C A D B.

RR (Round Robin) esegue i processi nellordine di arrivo e li interrompe una volta che
esauriscono il loro quanto di tempo: A (25,15) B (50,40) C (10,0) D (30,20) A (15,5)
B (40,30) D (20,10) A (5,0) B (30,20) D (10,0) B (20,0).
La notazione X(t1,t2) significa che il processo X prende controllo della CPU per (t1-t2)ms e una
volta rilasciata la CPU necessita ancora di t2ms per completare lesecuzione.

Il throughput un indicatore del numero di processi completati nellunit di tempo; si pu


calcolare come numero processitempo di esecuzione totale, anche se nelle applicazioni reali si
guarda quanti job sono stati completati in un intervallo prefissato di tempo (unora, un giorno,
).

Il tempo di turnaround lintervallo di tempo che intercorre tra larrivo di un processo nella coda
dei processi READY e il suo completamento.
Esercizio 8
Esercizio 9
Esercizio 10
Esercizio 11
Esercizio 12
Esercizio 13
Esercizio 14
Esercizio 15
Esercizio 16
Esercizio 17
Esercizio 18
Esercizio 19
Riferimenti & Bibliografia

Potrebbero piacerti anche