Sei sulla pagina 1di 32

CAPITOLO 8 – GESTIONE DEI PROCESSI

8.1 – I PROCESSI
Si chiama job la richiesta di elaborazione presentata da un utente al sistema di
elaborazione, per es. la richiesta di predisporre le buste paga dei dipendenti avanzata dal
responsabile dell’ufficio stipendi di una azienda.
L’elaborazione richiesta da un job e’ scomposta dal S.O. nella esecuzione di una successione
di attivita’, create dal S.O. stesso a seguito di SVC (Supervisor Call) e dette processi (in
inglese process o anche task). Un processo e’ quindi definibile come un programma in
esecuzione.
Da questa definizione deriva che un processo comprende tipicamente:
• un segmento di codice (la sezione del programma in esecuzione);
• la definizione dell’attivita’ corrente (attuata dal contenuto del Program Counter e degli
altri registri del Pc che si suppone unico: in questo capitolo, salvo indicazione
contraria, si suppone di avere a che fare con sistemi monoprocessori);
• una pila (“stack”) contenente i dati temporanei (variabili locali);
• l’insieme di dati del programma sul quale agisce il segmento di codice (variabili globali).
Ad uno stesso programma possono essere associati piu’ processi che differiscono tra loro
perche’ ne cambia qualche parte, per esempio le variabili globali. Alla stesso modo un
processo (detto genitore) ne puo’ originare altri (detti figli) attraverso una SVC.

Un processo durante la sua esistenza (esecuzione) cambia stato secondo il diagramma di fig.
8-1. Una volta creato dal sistema operativo nel momento in cui ha a disposizione tutte le
risorse che gli occorrono per andare in esecuzione viene posto nello stato “Ready” . Da
questo stato passa in “Running” allorche’ gli viene assegnato il processore centrale ed
interrompe l’esecuzione o perche’ ha esaurito l’intervallo di tempo (“quanto” o time slice)
assegnatogli, ed in questo caso ritorna nello stato “Ready”, oppure perche’ deve attendere il
risultato di una operazione di I/O da esso richiesta od il verificarsi di un qualche evento (per
es. l’arrivo di un messaggio; si veda piu’ avanti il paragrafo 8.6), ed in questo caso passa
nello stato “Waiting”. Si noti che l’operazione di I/O e’ svolta da un altro processo creato al
momento della richiesta. Dallo stato “Waiting” ritorna poi a “Ready” quando l’I/O e’ stato
completato o l’evento atteso si e’ verificato. In generale un processo puo’ riciclare piu’ volte
nella parte centrale del diagramma sino al momento in cui termina e sparisce dal sistema.

NEW TERMINATED

Ingresso Interrupt Uscita

READY RUNNING

Intervento
Completamento schedulatore
di I/O o processi Attesa di I/O
verificarsi di o di evento
evento

WAITING

Fig. 8-1. Diagramma degli stati di un processo


Capitolo 8 1
Il sistema operativo (S.O.) tiene traccia di un processo per mezzo di un Process Control
Block (PCB) che comprende tipicamente:
• l’identificatore del processo all’interno del sistema
• il puntatore al successivo processo se esiste una pila di processi in attesa di utilizzare
una determinata risorsa ( si veda piu’ avanti un esempio in fig. 8-3)
• l’individuazione dello stato del processo
• il contenuto del Program Counter e dei registri del Pc (accumulatore, registro indice,
registri di uso generale)
• informazioni sulla memoria usata dal processo (per esempio registro base e registro
barriera oppure PMT oppure SMT)
• informazioni sulla schedulazione quali priorita’ e durata del quanto (si veda il paragrafo
successivo)
• informazioni contabili (tempo di CPU usato, massimo tempo di CPU consentito,
imputazione della spesa)
• informazioni sull’attivita’ di I/O (dispositivi allocati, file aperti)

I processi si alternano nell’uso del Pc ed il passaggio da un processo ad un altro, curato dal


S.O., e’ detto context switch (come già precisato nel paragrafo 1.). La fig. 8-2 rappresenta
graficamente la transizione tra due processi P0 e P1, originata da un segnale di interruzione
o da una SVC. Negli attuali calcolatori il context switch richiede da 1 a 1000 µs.

Processo P0 Sistema operativo Processo P1

In
esecuzione
Interruzione o
chiamata al sistema
I
N
A
Salva dati in PCB0
T
T
I
V
O
Carica dati da PCB1
I
N
A In
T esecuzione
T Interruzione o
I chiamata al sistema
V
O
Salva dati in PCB1

I
N
A
Carica dati da PCB0 T
T
I
V
O
In
esecuzione

Fig. 8-2. “Context switch” tra due processi


(PCBi = Process Control Block del processo i)

Capitolo 8 2
8.2 SCHEDULAZIONE DEI PROCESSI
Con questo termine si individua l’attivita’ svolta dal S.O. per stabilire l’avvicendamento dei
processi. L’avvicendamento e’ effettuato tenendo conto delle code:
• dei processi nello stato “ready”
• dei processi in attesa di dispositivi
La fig. 8-3 rappresenta una possibile situazione di alcune code di un sistema.

PCB7 PCB2

CODA TESTA
STATO
READY END
CODA

CODA TESTA
NASTRO END
MAGN.
CODA

PCB1 PCB8 PCB6


END

CODA TESTA
DISCO
1 END
CODA

PCB4

CODA TESTA
DISCO
2 END
CODA

................

Fig. 8-3. Code per lo stato “Ready” e per i vari dispositivi

La schedulazione e’ effettuata da moduli del S.O. che costituiscono lo schedulatore e che


svolgono le seguenti funzioni:
• schedulatore a lungo termine
gestisce i job selezionandoli tra quelli presenti nella Ms e caricandoli nella Mp
interviene quindi con bassa frequenza e puo’ essere complesso e richiedere tempi
lunghi di funzionamento
puo’ essere utilizzato per bilanciare il carico sul sistema combinando opportunamente
job CPU-bound e I/O-bound (per la definizione di questi due termini si veda il
paragrafo 8.7)
• schedulatore a medio termine
rimuove dei processi dalla Mp per modificare la miscela di processi e migliorare le
prestazioni del sistema
• schedulatore a breve termine
gestisce i processi selezionandone uno tra quelli nello stato READY ed assegnando ad
esso il Pc
interviene quindi con alta frequenza e deve essere semplice e di funzionamento veloce

Capitolo 8 3
Lo schedulatore a breve termine deve essere sempre presente; lo schedulatore a lungo
termine e’ spesso presente mentre meno frequentemente e’ presente lo schedulatore a medio
termine.

La fig. 8-4 da’ una rappresentazione di massima degli schedulatori a breve e lungo termine
per mezzo di un diagramma a code nel quale i rettangoli rappresentano le code ed i cerchi le
“stazioni di servizio”. La fig. 8-5 aggiunge al diagramma di fig. 8-4 lo schedulatore a medio
termine.

Fig. 8-4. Schedulazione dei processi a breve ed a lungo termine

Fig. 8-5. Schedulazione dei processi a medio termine

8.3 CREAZIONE E COMPLETAMENTO DEI PROCESSI


I processi sono creati a seguito di SVC e formano tipicamente un albero. La fig. 8-6 da’ la
rappresentazione dell’albero dei processi in un tipico sistema operativo UNIX (root, init,
pagedaemon sono processi standard del sistema operativo).

Capitolo 8 4
ROOT

PAGE-
SWAPPER INIT
DAEMON

UTENTE 1 UTENTE 2 UTENTE 3

Fig. 8-6. Albero dei processi in un tipico sistema UNIX

Facendo riferimento a processi di tipo genitore e di tipo figlio le situazioni che si possono
presentare sono le seguenti:

(a) in termini di risorse


• il processo genitore puo’ suddividere le proprie risorse tra i figli
• il processo genitore ed i suoi figli condividono le risorse
• i processi figli possono ricevere risorse dal S.O.

(b) in termini di esecuzione


• il processo genitore viene sospeso ed attende il completamento dei figli
• il processo genitore ed i processi figli sono eseguiti insieme

(c) in termini di spazio degli indirizzi


• i processi figli hanno una copia di quello del genitore
• i processi figli ne hanno uno proprio

Il completamento dei processi avviene in due modi:


• in modo naturale: il processo completa la propria attivita’ e chiede con una SVC al S.O.
di cancellarlo, eventualmente trasmettendo dati al processo genitore;
• in modo forzato: il suo completamento e’ richiesto dal suo genitore, per es. perche’ il
genitore si completa oppure perche’ svolge un compito non piu’ necessario oppure perche’
ha raggiunto i limiti di risorse assegnatigli.

Capitolo 8 5
8.4 PROCESSI COOPERANTI
Si chiamano concorrenti i processi eseguiti insieme nel sistema.

I processi concorrenti possono essere:


• indipendenti
si tratta di processi che non influenzano e non sono influenzati da altri processi e che
quindi non condividono dati ne’ temporanei ne’ permanenti
• cooperanti
si tratta di processi che possono influenzare od essere influenzati da altri processi

La cooperazione tra processi concorrenti risulta conveniente per vari motivi:


• consente di condividere informazione tra processi (per es. un file)
• offre una maggiore flessibilita’ agli utenti gestendone in parallelo piu’ attivita’ (per es.
editing, stampa, compilazione) ovvero consente di accelerare l’esecuzione di calcoli in un
sistema multiprocessore (per es. suddividendoli in processi eseguiti in parallelo sulle varie
unita’ di elaborazione)
• consente una realizzazione modulare del software (per es. realizzando processi distinti
per le varie funzioni del S.O. quali caricamento ed esecuzione di programmi, supporto di
linguaggi di programmazione, manipolazione di file)

La cooperazione richiede l’introduzione di meccanismi per consentire la comunicazione tra


processi e per sincronizzare le loro azioni.

Un classico esempio di processi cooperanti e’ fornito dal cosiddetto problema produttore-


consumatore nel quale un processo, detto produttore, produce informazione che e’
utilizzata da un altro processo, detto consumatore.
Il funzionamento cooperante e’ ottenuto attraverso la definizione di un buffer, che svolge le
funzioni di un magazzino riempito dal processo produttore e svuotato dal processo
consumatore. Il buffer puo’ essere finito o illimitato e puo’ essere realizzato in due modi: o
dal programmatore attraverso l’uso di memoria condivisa o dal S.O.attraverso meccanismi di
comunicazione tra processi.
Vediamo una realizzazione con memoria condivisa. Le variabili condivise sono individuate
dalle seguenti dichiarazioni (qui e nel seguito del capitolo si fa uso di un linguaggio “C-
like”):

var n;
type item=…;
var buffer : array [0..n-1] of item;
in, out : 0..n-1;

Il buffer, finito, e’ realizzato per mezzo di un vettore circolare di n posizioni che contiene gli
elementi (di informazione) prodotti o consumati, denominati item e definiti nella
dichiarazione type; in ed out sono due puntatori, inizializzati al valore 0: in punta alla prima
posizione libera del buffer, out punta alla prima posizione occupata. Il buffer e’ vuoto se in =
out, e’ pieno se (in+1) mod n = out.
Il processo produttore, che pone il nuovo elemento prodotto nella variabile locale nextp e’
realizzabile nel seguente modo:
repeat
….
inserisci l’elemento prodotto in nextp
….
while in+1 mod n = out do no-op;
buffer[in] := nextp;
in := in+1 mod n;
until false;

Capitolo 8 6
Il costrutto repeat … until (espressione booleana) comporta la ripetizione delle operazioni
incluse tra repeat e until finche’ non diventa vera l’espressione booleana (nel nostro caso si
ha quindi una ripetizione indefinita delle operazioni incluse) mentre no-op corrisponde a non
far nulla.
Il processo consumatore, che pone nella variabile locale nextc l’elemento da consumare, e’
realizzabile nel seguente modo:

repeat
while in = out do no-op;
nextc := buffer[out];
out := out+1 mod n;
….
consuma l’elemento inserito in nextc
….
until false;

Si puo’ verificare che la realizzazione proposta consente di avere al piu’ n-1 elementi nel
buffer.

8.5 THREAD
Alcuni processi sono scomponibili in unita’ elementari di utilizzazione del Pc consistenti di
un Program Counter, un insieme di registri ed una pila: queste unita’ elementari, dette
thread, condividono con le altre unita’ elementari il segmento di codice, le variabili globali e
risorse del S.O. quali file aperti.
Una siffatta estesa condivisione consente di creare thread e di passare da un thread ad un
altro in modo veloce e poco oneroso rispetto al context switch in quanto in pratica e’
richiesto solo il caricamento di un insieme di registri, le altre parti essendo condivise.

Se un processo e’ suddiviso in piu’ thread occorre gestire la concorrenza anche dei thread. I
thread condividono il Pc e sono eseguiti in sequenza di modo che uno solo alla volta e’
attivo. Un thread attraversa gli stessi stati di un processo (fig.8-1) e come un processo puo’
creare thread figli. Quando un thread si blocca per effetto di una SVC un altro thread puo’
andare in esecuzione. Per esempio, i due processi produttore e consumatore prima visti
potrebbero essere considerati due thread di uno stesso processo.

I thread non sono indipendenti tra loro e possono leggere e scrivere nelle pile di altri thread.
Non esiste un meccanismo di protezione dei thread ma non e’ necessario in quanto, al
contrario di cio’ che si verifica per i processi che nascono tipicamente da utenti diversi,
esiste un unico proprietario del processo e dei suoi thread (l’utente la cui richiesta ha dato
origine al processo).

I thread possono essere gestiti dal S.O. in modi diversi:


(a) dal nucleo (kernel) del S.O., la parte cioe’ sempre presente nella Mp
in questo caso il S.O. mette a disposizione delle chiamate analoghe a quelle usate dai
processi e si parla di kernel-supported thread
il passaggio da un thread ad un altro e’ piu’ lento che nel caso (b) in quanto deve
essere eseguito dal nucleo ma e’ maggiore la velocita’ di esecuzione perche’ ogni thread
e’ gestito in modo autonomo
(b) da chiamate dell’utente ad una apposita libreria al di sopra del nucleo
in questo caso si parla di user-level thread
il passaggio da un thread ad un altro e’ piu’ veloce che nel caso (a) ma una SVC
effettuata da un thread puo’ bloccare l’intero processo perche’ il nucleo gestisce solo
processi ed un processo in attesa non riceve tempo di CPU
(c) in modo misto, combinando gli approcci sopra indicati in (a) e (b)
Capitolo 8 7
8.6 COMUNICAZIONE TRA PROCESSI
La cooperazione di due processi puo’ essere ottenuta realizzando una struttura di
comunicazione tra di essi che consenta di sincronizzare le loro azioni tipicamente attraverso
lo scambio di messaggi.
Una siffatta struttura deve fornire almeno le due operazioni basilari (o primitive)
send(messaggio)
receive(messaggio)

Se due processi P e Q vogliono comunicare tra di loro, ciascuno di essi deve poter inviare o
ricevere messaggi dall’altro. Occorre quindi predisporre sia un collegamento fisico (link),
ricorrendo ad una memoria condivisa o ad un bus o ad una rete, sia la sua gestione logica.
Quest’ultima deve affrontare problemi quali:
• le modalita’ per stabilire il collegamento
• la capacita’ del collegamento
• la dimensione dei messaggi
• le eventuali condizioni anomale che si possono presentare
Esamineremo ora nell’ordine questi problemi tenendo anche conto di aspetti collaterali quali
il numero di processi che possono comunicare tra loro, il numero di collegamenti che
possono esistere tra due processi e la unidirezionalita’ o bidirezionalita’ dei collegamenti

8.6.1 MODALITA’ PER STABILIRE IL COLLEGAMENTO

Due sono le possibili modalita’:


(a) comunicazione diretta;
(b) comunicazione indiretta.

(a) Nel caso di comunicazione diretta un processo deve indicare esplicitamente il mittente o
il destinatario della comunicazione. Le primitive precedentemente introdotte sono
modificate come segue:
send(P, messaggio) per inviare un messaggio al processo P
receive(Q, messaggio) per ricevere un messaggio dal processo Q
Da questa struttura derivano le seguenti proprieta’:
• si stabilisce un collegamento tra ogni coppia di processi che vogliono comunicare e che
conoscono i reciproci identificatori
• si associa uno ed un solo collegamento ad ogni coppia di processi
• il collegamento e’ solitamente bidirezionale

Utilizzando questo schema il problema produttore-consumatore puo’ essere risolto con due
processi cooperanti: il produttore puo’ produrre un elemento mentre il consumatore ne
consuma un altro. La loro struttura e’ cosi’ definita:
processo produttore repeat

produce un elemento e lo inserisce in nextp

send(consumatore, nextp);
until false;

processo consumatore repeat


receive(produttore, nextc);

consuma l’elemento in nextc

until false;
Capitolo 8 8
Lo schema di indirizzamento che si ottiene in questo modo e’ simmetrico nel senso che gli
identificatori dei due processi P e Q possono essere scambiati tra di loro, sempre che cio’
abbia senso.
E’ possibile realizzare uno schema di collegamento asimmetrico che usa le primitive
send(P, messaggio) per inviare un messaggio al processo P
receive(id, messaggio) per ricevere un messaggio da un qualsiasi processo
(id assume il nome del processo con il quale e’ stata fatta la
comunicazione)
Sia lo schema simmetrico che quello asimmetrico presentano l’inconveniente di una limitata
modularita’ nel senso che se si cambia il nome di un processo occorre modificare le
definizioni di tutti i processi in comunicazione con esso.

(b) Nel caso di comunicazione indiretta i messaggi sono inviati e ricevuti utilizzando delle
cassette postali (c.p. o mailbox) costituite da aree riservate nella Mp. Ogni c.p. ha un
identificatore univoco.
Le primitive di comunicazione diventano:
send(A, messaggio) invia un messaggio alla c.p. A
receive(A, messaggio) ricevi un messaggio dalla c.p. A
Ne risultano le seguenti proprieta’:
• due processi possono comunicare solo se condividono una cassetta postale
• il collegamento puo’ essere associato a piu’ di due processi
• tra due processi si possono avere piu’ collegamenti, ciascuno associato ad una diversa
c.p.
• il collegamento puo’ essere unidirezionale o bidirezionale

Una c.p. puo’ essere:


• posseduta da un processo
• posseduta dal S.O.

Se una c.p. e’ posseduta da un processo, il proprietario e’ il processo che la definisce, per es.
con una variabile di tipo mailbox. Il processo che la definisce puo’ solo ricevere messaggi; i
processi utilizzatori sono tutti i processi che conoscono l’identificatore della c.p. e possono
solo inviare messaggi nella c.p.; dato che la c.p. ha un unico proprietario non vi sono
incertezze sul processo che deve ricevere il messaggio.
Una c.p. sparisce quando termina il processo che la possiede e dopo la sparizione i processi
che eventualmente inviassero messaggi a tale c.p. devono essere avvertiti a cura del S.O..

Se una c.p. e’ posseduta dal S.O. il S.O. deve prevedere un meccanismo che consenta ad un
processo
- di creare una c.p.
- di inviare e ricevere messaggi
- di distruggere una c.p.
Il processo che crea una c.p. ne e’ il proprietario ed e’ l’unico processo che puo’ ricevere
messaggi in essa. La proprieta’ di una c.p. puo’ pero’ essere estesa ad altri processi per
mezzo di SVC od attraverso la creazione di processi “figlio” (se il processo P ha creato la c.p.
A e poi origina il processo Q, P e Q possono condividere la c.p. A).
Dato che tutti i processi che usano una c.p. prima o poi finiscono, il S.O. deve
periodicamente fare pulizia ricuperando lo spazio in Mp dedicato alle c.p. non piu’ usate.

8.6.2 DIMENSIONE DEI MESSAGGI

I messaggi inviati da un processo possono avere dimensioni fisse o variabili.


Se la dimensione e’ fissa, risulta piu’ semplice la realizzazione fisica del collegamento ma e’
piu’ difficile la programmazione; se la dimensione e’ variabile risulta al contrario piu’
complessa la realizzazione fisica ma piu’ semplice la programmazione.

Capitolo 8 9
8.6.3 CAPACITA’ DEL COLLEGAMENTO

La capacita’ di un collegamento e’ individuata dal numero di messaggi che vi possono


risiedere transitoriamente e che formano una coda associata al collegamento stesso. Tipiche
alternative sono le seguenti:
• collegamento senza buffer (a capacita’ nulla)
• collegamento con buffer a capacita’ finita
• collegamento con buffer a capacita’ illimitata

Nel caso di collegamento senza buffer evidentemente non vi possono essere messaggi in
attesa. Il processo mittente deve aspettare che il processo destinatario riceva il messaggio
precedente prima di poterne inviare un altro. I due processi devono quindi essere
sincronizzati ed il meccanismo di comunicazione e’ detto “rendezvous”.

Nel caso di collegamento con buffer a capacita’ finita la coda puo’ contenere al piu’ un certo
numero n di messaggi. Quando un processo mittente invia un messaggio, se la coda e’ piena
deve restare in attesa, se la coda non e’ piena il messaggio e’ aggiunto alla coda ed il
processo mittente puo’ proseguire l’esecuzione.

Nel caso di collegamento con buffer a capacita’ illimitata non vi sono ovviamente limiti al
numero di messaggi che possono essere ospitati nella coda e di conseguenza il processo
mittente non deve mai attendere.

Nel caso di entrambi i collegamenti con buffer il processo mittente non e’ in grado di sapere
se un messaggio e’ arrivato a destinazione. Se questa informazione e’ essenziale per
proseguire nella elaborazione occorre prevedere una segnalazione esplicita dell’avvenuta
ricezione. Questo si puo’ ottenere facendo si’ che il processo mittente P esegua le operazioni
send(Q, messaggio)
receive(Q, messaggio)
e che il processo destinatario Q a sua volta esegua le operazioni
receive(P, messaggio)
send(P, “acknowledgment”)
In questo caso si dice che i due processi comunicano asincronicamente.

Tra le altre possibili modalita’ di gestione dei messaggi si puo’ ricordare il meccanismo detto
“chiamata remota di procedura” o RPC (Remote Procedure Call). In un sistema
monoprocessore una chiamata di procedura fa si’ che il programma chiamante si blocchi in
attesa di ricevere il risultato elaborato dalla procedura chiamata. Per analogia si puo’
realizzare un collegamento sincrono tra processi cooperanti consentendo ad essi di
chiamarsi a vicenda per mezzo di una RPC come fossero procedure.

8.6.4 CONDIZIONI ANOMALE

Nella comunicazione tra processi si possono presentare situazioni anomale originate da


problemi collegati per esempio alle linee di comunicazione (questo e’ particolarmente vero
nei sistemi distribuiti nei quali i processi possono risiedere in calcolatori diversi,
eventualmente anche collocati in luoghi diversi). Si esamineranno tre condizioni anomale:

(a) terminazione inaspettata di un processo


(b) perdita di messaggi
(c) ricezione di messaggio illeggibile

(a) Sia un processo mittente che un processo destinatario possono terminare prima che sia
stato elaborato un messaggio: di conseguenza si possono avere messaggi che non verranno
mai ricevuti e processi che attendono messaggi che non verranno mai inviati.

Capitolo 8 10
Esaminiamo il primo caso. Si supponga che un processo P invii un messaggio ad un
processo Q che e’ terminato. Nel caso di collegamento senza buffer P resta bloccato e tocca
al S.O. far terminare P od informarlo che Q e’ terminato. Nel caso di collegamento con buffer
P puo’ continuare con l’esecuzione a meno che non abbia necessita’ di sapere che il
messaggio e’ stato elaborato: in questo caso tocca al S.O. informare P che Q e’ terminato.
Esaminiamo il secondo caso. Si supponga che un processo P sia in attesa di un messaggio
da un processo Q che e’ terminato. Per evitare che P resti indefinitamente in attesa deve
intervenire il S.O. che o forza la terminazione anche di P o lo informa che Q e’ terminato.

(b) Un messaggio puo’ andare perso a seguito di problemi nelle apparecchiature o nelle linee
di trasmissione. Non sempre e’ necessario sapere se un messaggio e’ andato perso: tocca
all’utente specificare se occorre individuare una eventuale perdita informandone il S.O.
oppure programmando il controllo relativo.
Nel primo caso (coinvolgimento del S.O.) gli approcci tipici sono i seguenti:
- il S.O. rileva la perdita e ritrasmette il messaggio;
- il S.O. rileva la perdita e la segnala al processo mittente che decide cosa fare (per es.
ritrasmettere il messaggio)
Nel secondo caso (gestione a carico del programmatore) il processo mittente, rilevata la
perdita, si fa carico di ritrasmettere il messaggio.
Il meccanismo piu’ comunemente utilizzato per scoprire se un messaggio si e’ perso e’ il
cosiddetto “timeout”. Esso prevede che, quando si invia un messaggio, ne sia sempre
confermato il ricevimento. Il S.O. od il processo mittente possono allora stabilire un
intervallo di tempo entro il quale ci si aspetta che arrivi il messaggio di conferma
dell’avvenuto ricevimento. Se tale intervallo trascorre senza conferma, si presume che il
messaggio sia andato perso ed eventualmente lo si rinvia. Se per caso il messaggio fosse solo
in ritardo, si potrebbero avere piu’ copie dello stesso messaggio ed occorre quindi prevedere
un meccanismo per rilevare questa situazione.

(c) Un messaggio puo’ risultare illeggibile per esempio a causa di rumore sul canale
trasmissivo. La condizione di illeggibilita’ deve essere rilevata con opportuni meccanismi (per
es. codice di parita’, somma di controllo, controllo di ridondanza ciclica) ed in questo caso il
S.O. si cura di far ritrasmettere il messaggio.

8.7 GENERALITA’ SULLA GESTIONE DEL PROCESSORE CENTRALE


Come e’ gia’ stato fatto rilevare, la multiprogrammazione e’ stata introdotta per migliorare lo
sfruttamento del Pc. Consentendo infatti che piu’ processi siano ospitati nella Mp, ogni volta
che uno di essi interrompe l’esecuzione, tipicamente per far fronte ad un richiesta di I/O, il
S.O. puo’ assegnare il Pc ad un altro dei processi presenti in memoria. Questo meccanismo
di gestione del Pc funziona bene perche’ nell’esecuzione di un processo si alternano intervalli
(burst) di uso del Pc ed intervalli di attesa per l’I/O, come esemplificato in fig. 8-7.
Ovviamente l’esecuzione di un processo comincia e termina con un intervallo di uso del Pc.

Sperimentalmente si e’ rilevato che la durata degli intervalli di uso del Pc varia molto con i
processi ed i calcolatori ma che la loro distribuzione e’ simile a quella di fig. 8-8
(distribuzione di Poisson). Le durate potrebbero essere espresse per esempio in millisecondi.
Con riferimento a tale distribuzione si parla di esecuzione I/O-bound se sono presenti molti
intervalli assai brevi e di esecuzione CPU-bound se sono presenti parecchi intervalli molto
lunghi.

Capitolo 8 11

(istruzioni varie)
Intervallo di impiego del Pc

leggi da file

attesa per il completamento di I/O Intervallo di I/O


(istruzioni varie)
… Intervallo di impiego del Pc
scrivi su file

attesa per il completamento di I/O Intervallo di I/O



(istruzioni varie)
… Intervallo di impiego del Pc
leggi da file

attesa per il completamento di I/O Intervallo di I/O


Intervallo di impiego del Pc

Fig. 8-7 – Alternanza degli intervalli di impiego di Pc ed I/O

160
140
(numero di intervalli)

120
Frequenza

100
80
60
40
20
0
0 10 20 30 40 50
Durata intervalli

Fig. 8-8 – Distribuzione degli intervalli di impiego del Pc

Il modulo del S.O. che sceglie, nella coda dei processi nello stato READY, il processo al quale
assegnare il Pc e’ detto schedulatore dei processi o schedulatore della CPU ed e’ lo
schedulatore a breve termine prima introdotto (paragrafo 8.2).
Lo schedulatore dei processi e’ affiancato da un modulo, denominato controllore del traffico
(dispatcher), che ha il compito di eseguire materialmente il passaggio del controllo del Pc al
processo che subentra. Esso deve attuare il context switch, ripristinare il modo utente del
S.O. e far ripartire il programma dalla posizione nella quale si era fermato. Data l’elevata
frequenza di intervento il tempo che questo modulo impiega per svolgere le predette
operazioni, detto dispatch latency, deve essere molto breve.

Lo schedulatore dei processi puo’ intervenire in quattro circostanze:


Capitolo 8 12
1. quando un processo passa dallo stato RUNNING a WAITING
2. quando un processo termina
3. quando un processo passa dallo stato RUNNING a READY
4. quando un processo passa dallo stato WAITING a READY
Se lo schedulatore interviene solo nelle circostanze 1. e 2. la schedulazione e’ detta “non-
preemptive”. In pratica un processo usa il Pc sinche’ o termina o avvia una operazione di
I/O.
Se lo schedulatore gestisce tutte e quattro le circostanze la schedulazione e’ detta
“preemptive” ed e’ piu’ complessa da gestire, dovendo per esempio coordinare gli accessi a
dati condivisi e fare in modo che il nucleo del S.O. non venga messo in crisi dagli
avvicendamenti dei processi.

8.8 LA SCHEDULAZIONE DELL’USO DEL PROCESSORE CENTRALE


8.8.1 CRITERI DI SCHEDULAZIONE

I parametri piu’ comunemente utilizzati per confrontare gli algoritmi utilizzati dallo
schedulatore dei processi sono:
1. percentuale di uso del Pc
2. throughput (n° di processi completati nell’unita’ di tempo)
3. tempo di risposta dei processi o semplicemente tempo di risposta (dalla loro
presentazione al loro completamento)
4. tempo di attesa dei processi (tempo da essi trascorso nella coda dello stato READY)
5. tempo di risposta del sistema (dal termine della presentazione di una richiesta all’inizio
della risposta del sistema)
Ovviamente si deve tendere a massimizzare il valore dei parametri 1. e 2. ed a minimizzare
quello degli altri tre parametri. Per lo piu’ si cerca di ottimizzare il valor medio ma in alcuni
casi e’ preferibile ottimizzare il valore minimo o massimo. Per esempio, nel caso del tempo di
risposta del sistema spesso si cerca di minimizzare il valore massimo. Nei sistemi interattivi
e’ in effetti considerato piu’ importante minimizzare la varianza dei parametri ossia la
somma dei quadrati dei loro scostamenti dal valor medio.

8.8.2 ALGORITMI

Tra i numerosi algoritmi proposti esamineremo i seguenti:


(a) First Come First Served (FCFS)
(b) Shortest Job First (SJF)
(c) Priorita’
(d) Round Robin (RR)
(e) Code a piu’ livelli
(f) Code a piu’ livelli con feedback
Per semplicita’ negli esempi numerici che seguono, salvo diverse precisazioni, per ogni
processo si prendera’ in esame un solo intervallo di uso del Pc.

(a) FCFS

Il Pc e’assegnato al processo che lo chiede per primo. L’algoritmo puo’ essere realizzato
gestendo i processi nello stato READY come una coda FIFO: quando un processo entra nella
coda il suo PCB e’ collegato all’ultimo elemento della coda; quando il Pc e’ libero, viene
assegnato al processo in testa alla coda.
L’algoritmo e’ non-preemptive ed e’ semplice da realizzare ma il valore medio del tempo di
attesa e’ in genere peggiore che per altri algoritmi e puo’ variare molto se gli intervalli di uso
del Pc variano molto. Si consideri al riguardo il caso di tre processi caratterizzati dagli

Capitolo 8 13
intervalli di uso del Pc definiti nella seguente tabella ed espressi in unita’ arbitrarie
(potrebbero essere per esempio millisecondi):

Processo Intervallo di uso del


Pc
P1 24
P2 3
P3 3

I tre processi si immaginano tutti arrivati allo stesso istante (istante 0). La fig. 8-9 riporta i
diagrammi (di Gantt) che illustrano lo sviluppo dell’esecuzione sia nel caso di considerare i
processi nell’ordine P1, P2, P3 sia nell’ordine P2, P3, P1. Nel primo caso si ottiene (in questo e
negli esempi che seguono i valori riportati corrispondono ordinatamente ai processi
nell’ordine 1 → 2 → 3 → ….)
valore medio del tempo di risposta = (24 + 27 +30)/3 = 27
valore medio del tempo di attesa = (0 + 24 + 27)/3 = 17
mentre nel secondo caso si ha
valore medio del tempo di risposta = (3 + 6 + 30)/3 = 13
valore medio del tempo di attesa = (0 + 3 + 6)/3 = 3

Primo caso
P1 P2 P3 Tempo
0 24 27 30

Secondo caso
P2 P3 P1
Tempo
0 3 6 30

Fig. 8.9 – Applicazioni dell’algoritmo FCFS

L’algoritmo puo’ dar luogo ad ulteriori inconvenienti in situazioni particolari (per esempio
quando un processo CPU-bound coesiste con molti processi I/O-bound).

(b) SJF

Il Pc e’ assegnato al processo che richiede il successivo piu’ breve intervallo di uso del Pc
stesso; in effetti il nome corretto dovrebbe essere “shortest next CPU burst”. A parita’ di
intervallo si ricorre all’algoritmo FCFS. Il nome gli deriva dalla sua originaria applicazione ai
job (schedulazione a lungo termine), nella quale si usa come parametro la durata del job
stimata dall’utente quando sottopone il job al sistema.
Si puo’ dimostrare che l’algoritmo SJF e’ ottimale, nel senso che minimizza il valore medio
del tempo di attesa per un insieme assegnato di intervalli.
Si considerino per esempio quattro processi caratterizzati come in tabella ed arrivati
nell’ordine ma tutti all’istante 0:
Processo Intervallo di uso del
Pc
P1 6
P2 8
P3 7
P4 3

La fig. 8-10 riporta il diagramma di esecuzione dal quale si ricava:


valore medio del tempo di risposta = (3 + 9 + 16 + 24)/4 = 13
Capitolo 8 14
valore medio del tempo di attesa = (0 + 3 + 9 +16)/4 = 7
Si noti che con l’algoritmo FCFS i valori corrispondenti sarebbero stati rispettivamente
65/4 = 16.25 e 41/4 = 10.25.

P4 P1 P3 P2 Tempo
0 3 9 16 24

Fig. 8-10 – Applicazione dell’algoritmo SJF

La difficolta’ nell’applicare in modo rigoroso l’algoritmo SJF sta nel fatto che in pratica non e’
nota a priori la durata del successivo intervallo di uso del Pc. Si puo’ quindi applicarlo in
modo approssimato introducendo un algoritmo per cercare di predire la durata del
successivo intervallo.
Discreti risultati si ottengono utilizzando a questo scopo la media esponenziale delle durate
misurate (e quindi note) per i precedenti intervalli di ogni processo. La formula ricorrente
che la definisce e’

τn+1 = α tn + (1-α) τn (0 ⊆ α ⊆1; n = 1,2,….)

in cui tn e’ il valore misurato dell’ultimo intervallo, τn e’ il valore stimato dello stesso


intervallo e τn+1 e’ il valore stimato del successivo intervallo. Per avviare la ricorsione τ1 e’
posto pari al valore medio degli intervalli a livello di sistema o ad una ragionevole costante e
di solito si assume α = 0.5. Si noti che se α = 0 il comportamento effettivo non ha alcuna
influenza mentre se α = 1 la storia passata diventa irrilevante.
Se si espande la formula per τn+1 si ottiene

τn+1 = α tn + (1-α) α tn-1 + …. + (1-α)j α tn-j + … + (1-α)n τ1

e, dato che in generale α ed 1-α sono < 1, partendo da sinistra i termini del secondo membro
hanno via via sempre meno peso (come e’ ragionevole che sia).
La fig. 8.11 illustra l’applicazione della formula per il caso α = 0.5 e per un processo per il
quale si è assunto τ1 = 10. Le successioni di valori risultano le seguenti:
τ1 = 10, τ2 = 8, τ3 = 6, τ4 = 6, τ5 = 5, τ6 = 9, τ7 = 11, τ8 = 12, …..
t1 = 6, t2 = 4, t3 = 6, t4 = 4, t5 = 13, t6 = 13, t7 = 13, t8 = 13, …..

14
12
10
8 Durate stimate
6 Durate effettive
4
2
0
0 2 4 6 8 10
Indice intervallo

Fig. 8-11 – Applicazione della media esponenziale

Capitolo 8 15
L’algoritmo SJF puo’ essere realizzato in modo non-preemptive (cioè senza diritto di
prelazione) ed in modo preemptive (con diritto di prelazione; in questo caso viene anche
denominato, in modo più preciso, “shortest remaining time first”). Le due realizzazioni
differiscono nel comportamento allorche’ arriva nella coda dello stato READY un processo il
cui prossimo intervallo di tempo e’ piu’ breve dell’intervallo di Pc restante per il processo in
quel momento in esecuzione: nel caso non-preemptive il processo in esecuzione completa il
suo intervallo, nel caso preemptive il nuovo arrivato va subito in esecuzione.
La fig. 8-12 riporta il diagramma di esecuzione per la gestione in modo preemptive dei
processi:

Processo Istante di Intervallo di uso del


arrivo Pc
P1 0 8
P2 1 4
P3 2 9
P4 3 5

P1 P2 P4 P1 P3 Tempo
0 1 5 10 17 26

Fig. 8-12 – Algoritmo SJF preemptive (shortest remaining time first)

Ne risultano i valori
valore medio del tempo di risposta = (17 + 4 + 24 + 7)/4 = 13
valore medio del tempo di attesa = (9 + 0 + 15 + 2)/4 = 6.5
Se gli stessi processi fossero stati gestiti in modo non-preemptive la successione delle
esecuzioni sarebbe stata P1 → P2 → P4 → P3 e si sarebbero ottenuti
valore medio del tempo di risposta = (8 + 11 + 24 + 14)/4 = 14.25
valore medio del tempo di attesa = (0 + 7 + 15 + 9)/4 = 7.75

(c) PRIORITA’

Il Pc e’ assegnato al processo con la priorita’ piu’ alta e, a pari priorita’, si ricorre


all’algoritmo FCFS.
In effetti l’algoritmo SJF puo’ essere visto come un algoritmo a priorita’ se si definisce come
priorita’ l’inverso della durata dell’intervallo stimato e si considera la priorita’ tanto maggiore
quanto maggiore e’ il valore dell’inverso.
All’interno di un calcolatore le priorita’ sono individuate da numeri interi e
convenzionalmente la priorita’ si considera tanto piu’ alta quanto piu’ basso e’ il numero che
la rappresenta.

Si consideri l’insieme di processi riportati nella sottostante tabella, tutti arrivati all’istante 0
ma nell’ordine indicato:

Processo Priorita’ Intervallo di uso del


Pc
P1 3 10
P2 1 1
P3 3 2
P4 4 1
P5 2 5

Utilizzando l’algoritmo basato sulle priorita’ il corrispondente diagramma di esecuzione


risulta
Capitolo 8 16
P2 P5 P1 P3 P4 Tempo
0 1 6 16 18 19

Fig. 8-13 – Algoritmo con priorita’

ed i parametri tipici assumono i valori seguenti:


valore medio del tempo di risposta = (16 + 1 + 18 + 19 + 6)/5 = 12
valore medio del tempo di attesa = (6 + 0 + 16 + 18 + 1)/5 = 8.2

Le priorita’ possono essere definite internamente al S.O. od al suo esterno.


Se sono definite internamente al sistema, esse sono basate su parametri misurabili (per es.
limiti di tempo di esecuzione, quantita’ di memoria richiesta); se sono definite all’esterno del
sistema, esse sono basate su criteri non controllati dal S.O. (per es. importanza del
processo, importanza del proprietario del processo, tipo di fondi utilizzati per pagare l’uso
delle risorse del sistema).
Anche questo algoritmo puo’ essere gestito in modo non-preemptive ed in modo preemptive.

Un problema che puo’ nascere con l’algoritmo a priorita’ e’ il cosiddetto blocco indefinito
(anche detto, con terminologia colorita, “morte per fame”). Si tratta di questo: un processo a
bassa priorita’ corre il rischio di essere posposto indefinitamente se continuano ad arrivare
processi a piu’ alta priorita’. Per ovviare all’inconveniente si puo’ ricorrere alla tecnica
dell’invecchiamento (aging) che aumenta gradualmente in modo automatico la priorita’ dei
processi che sono da tempo in attesa (per esempio di una unita’ ogni 15’ di attesa).

(d) ROUND ROBIN

Questo algoritmo prevede che all’interno del S.O. sia definito un intervallo di tempo di breve
durata (generalmente tra 10 e 100 ms) denominato quanto (di tempo) o “time slice”. La coda
associata allo stato READY e’ gestita come una coda circolare con disciplina FIFO (i nuovi
processi sono aggiunti in fondo alla coda) e lo schedulatore del Pc la percorre assegnando
ad ogni processo un intervallo di durata pari ad un quanto.
Se il processo al quale e’ assegnato il Pc ha un intervallo di esecuzione di durata minore di
un quanto, esso al termine dell’intervallo rilascia il Pc e lo schedulatore sceglie il successivo
processo nella coda.
Se, invece, il processo al quale e’ assegnato il Pc ha un intervallo di esecuzione di durata
maggiore di un quanto, al termine del quanto la sua esecuzione e’ interrotta dal S.O., il
processo e’ posto in fondo alla coda e lo schedulatore sceglie dalla coda il successivo
processo.
L’algoritmo e’ evidentemente preemptive.

Si consideri un esempio. Sono dati i tre processi della sottostante tabella, tutti arrivati
all’istante 0 ma nell’ordine indicato:

Processo Intervallo di uso del


Pc
P1 24
P2 3
P3 3

Supponendo di adottare un quanto di durata 4 il diagramma di esecuzione e’ quello


riportato in fig. 8-14 e ne risultano:
valore medio del tempo di risposta = (30 + 7 + 10)/3 = 15.67
valore medio del tempo di attesa = (6 + 4 + 7)/3 = 5.67

Capitolo 8 17
P1 P2 P3 P1 P1 P1 P1 P1
Tempo
0 4 7 10 14 18 22 26 30

Fig. 8-14 – Algoritmo round robin (quanto = 4)

Le prestazioni dell’algoritmo Round Robin dipendono molto dalla durata del quanto. Se la
durata e’ molto grande, l’algoritmo coincide praticamente con il FCFS; se la durata e’ molto
piccola (per es. 1 µs) l’algoritmo viene detto processor sharing in quanto in presenza di n
processi ognuno di essi sembra fruire di un processore che ha velocita’ pari ad 1/n di quella
del processore reale.

La durata del quanto influisce anche sul tempo di risposta. Si considerino i seguenti
processi:
Processo Intervallo di uso del
Pc
P1 6
P2 3
P3 1
P4 7

La fig. 8-15 mostra che per essi il valor medio del tempo di risposta non necessariamente
migliora al crescere del quanto, per lo meno sino a quando non si raggiungono valori tali far
si’ che la maggior parte dei processi utilizzi un solo quanto. Una regola empirica per fissare
il valore del quanto e’ la seguente: la durata del quanto deve essere tale che l’80% degli
intervalli di uso del Pc risulti minore del quanto.

12,4
12,2
Valor medio del tempo di

12
11,8
risposta

11,6
11,4
11,2
11
10,8
10,6
10,4
0 2 4 6 8
Durata del quanto

Fig. 8-15 – Variazione del tempo di risposta con la durata del quanto

Le prestazioni dell’algoritmo sono anche influenzate dall’incidenza dei context switch. Dato
che i context switch rallentano l’esecuzione, la durata del quanto deve essere grande rispetto
al tempo richiesto dal context switch (se quest’ultimo e’ pari al 10% della durata del quanto,
il 10% del tempo del Pc sara’ speso in context swich).
La fig. 8-16 mostra un esempio di come varia il numero di context switch in funzione della
durata del quanto, supponendo di avere a che fare con un processo il cui intervallo di
impiego del Pc e’ di 10 unita’.

Capitolo 8 18
Durata Diagramma di esecuzione Numero di
del quanto context
switch

12 0
0 1
0

6 1
0 6 1
0

1 9
0 1 2 3 4 5 6 7 8 9 1
0

Fig. 8-16 – Influenza della durata del quanto sul numero di context switch
(intervallo di impiego del Pc di 10 unita’)

(e) CODE A PIU’ LIVELLI

La coda dei processi nello stato READY e’ suddivisa in diverse code distinte ed i processi
sono assegnati ad una delle code in base alle loro caratteristiche (per es. memoria richiesta,
priorita’, tipo di processo). Ogni coda e’ gestita con un suo algoritmo e le varie code sono
solitamente gestite con un algoritmo a priorita’ fisse di tipo preemptive.
Un esempio di realizzazione di questo genere e’ illustrato in fig. 8-17.

Priorita’ massima

Processi di sistema

Processi interattivi

Processi interattivi di “editing”

Processi batch

Priorita’ minima

Fig. 8-17. Schedulazione con piu’ livelli di code

Un altro approccio prevede che ogni coda riceva una frazione predeterminata del tempo di
Pc. Se, per es., i processi fossero suddivisi in due gruppi, processi interattivi (o di
foreground) e batch (o di background), la coda dei processi interattivi potrebbe ricevere
l’80% del tempo di Pc ed usare l’algoritmo Round Robin e la coda dei processi batch ricevere
il restante 20% del tempo di Pc ed usare l’algoritmo FCFS.

(f) CODE A PIU’ LIVELLI CON FEEDBACK

In questo algoritmo di gestione i processi sono suddivisi in piu’ code ma si possono spostare
da una coda all’altra, principalmente per evitare la morte per fame.

Capitolo 8 19
Un esempio di siffatto schedulatore e’ riportato in fig. 8-18. Esso ha tre code denominate A,
B, C ed i processi in arrivo vengono inseriti tutti nella coda A. Le code A e B sono gestite in
modo preemptive e ricevono quanti di diversa durata mentre la coda C e’ gestita con
l’algoritmo FCFS. Lo schedulatore manda in esecuzione tutti i processi nella coda A e solo
quando essa e’ vuota passa alla coda B e passa alla coda C solo quando anche la coda B e’
vuota. Nell’esempio, se un processo nella coda A con completa il suo intervallo di impiego
del Pc in 8 unita’ e’ trasferito in fondo alla coda B; analogamente se un processo nella coda
B non completa il suo intervallo di impiego del Pc in 16 unita’ e’ trasferito in fondo alla coda
C.

Coda A: quanto = 8

Coda B: quanto = 16

Coda C: FCFS

Fig. 8.18 – Esempio di code a piu’ livelli con feedback

L’algoritmo a code con feedback e’ certamente il piu’ flessibile ed e’ anche il piu’ complesso
da realizzare in quanto richiede, nel caso piu’ generale, la definizione di vari parametri:
- numero di code
- algoritmo per ciascuna coda
- criterio per stabilire in quale coda inserire un nuovo processo
- criterio per stabilire quando un processo passa ad una coda piu’ bassa
- criterio per stabilire quando un processo passa aduna coda piu’ alta

8.9 SCHEDULAZIONE DEI MULTIPROCESSORI


Per semplicita’ si consideri il caso di sistemi a multiprocessore con processori omogenei in
termini di funzionalita’ (nel senso che qualsiasi processo puo’ andare in esecuzione su
qualsiasi processore). L’obiettivo primario della schedulazione e’ quello di suddividere il
carico di lavoro tra i vari processori in modo da tenerli tutti impegnati il piu’ possibile. A
questo scopo si realizza una coda per lo stato READY comune a tutti i processori. Dato che
ogni processore e’ libero di accedere alla coda e di prelevare da essa un processo da
eseguire, occorre realizzare il meccanismo di accesso e di aggiornamento della struttura dati
comune in modo da evitare che due processori scelgano lo stesso processo o che dei processi
vengano saltati e non eseguiti.
Per evitare questi problemi si possono adottare due soluzioni: (a) fare in modo che uno dei
processori programmi l’attivita’ degli altri (organizzazione master-slave); (b) affidare ad un
processore non solo le decisioni sulla schedulazione ma anche la gestione dell’I/O ed in
generale tutte le attivita’ di sistema, lasciando agli altri processori solo il compito di eseguire
codice utente (multiprocessing asimmetrico).

8.10 SCHEDULAZIONE PER IL TEMPO REALE


Si e’ gia’ rilevato che l’elaborazione in tempo reale si suddivide in due tipi: hard e soft.
Nei sistemi di tipo hard un processo considerato “critico” deve essere completato entro un
intervallo di tempo prestabilito. In questi sistemi un processo viene presentato con
l’indicazione del tempo entro il quale deve finire e lo schedulatore dei processi dovrebbe
Capitolo 8 20
accettare il processo solo se e’ in grado di dare la richiesta garanzia. Purtroppo e’ impossibile
dare una tale garanzia nei sistemi con Ms e/o memoria virtuale. Pertanto i sistemi in tempo
reale di tipo hard usano software sviluppato ad hoc che gira su hardware a loro dedicato e
non hanno tutte le funzionalita’ dei moderni sistemi operativi.
Nei sistemi di tipo soft e’ sufficiente che i processi critici ricevano priorita’ rispetto agli altri
processi. Lo schedulatore dei processiin questo caso va progettato in modo da soddisfare
due categorie di esigenze:
- deve essere in grado di gestire priorita’ assegnando ai processi in tempo reale la massima
priorita’ e non degradandola nel tempo;
- deve essere breve il tempo, denominato dispatch latency, che lo schedulatore impiega
per fermare un processo e farne partire un altro.
Mentre e’ relativamente facile soddisfare la prima categoria di esigenze, per esempio
eliminando il meccanismo dell’invecchiamento, e’ piu’ difficile soddisfare la seconda. In
effetti, dato che durante la commutazione da un processo ad un altro puo’ accadere che si
abbia da completare una chiamata al sistema o da eseguire un trasferimento coinvolgente
l’I/O, occorre introdurre un meccanismo di tipo preemptive.
A questo scopo si possono introdurre dei “punti di presvuotamento” nelle SVC di lunga
durata, in corrispondenza dei momenti nei quali non vengono modificate le strutture dati del
nucleo del S.O; il problema e’ che di siffatti punti se ne possono introdurre pochi e che di
conseguenza la dispatch latency puo’ rimanere troppo lunga.
In alternativa si puo’ rendere svuotabile l’intero nucleo, proteggendo le strutture dati in
corso di aggiornamento da modifiche da parte del processo piu’ prioritario per mezzo di
opportuni meccanismi di sincronizzazione. Nel caso in cui il processo piu’ prioritario che
subentra debba peraltro leggere o modificare dati del nucleo che sono stati protetti, si
ricorre al protocollo detto “eredita’ della priorita’” in base al quale i processi che accedono a
risorse richieste dal processo piu’ prioritario ne assumono temporaneamente la priorita’ in
modo da poter completare le operazioni su tali risorse.
Il funzionamento di un sistema di secondo questo tipo e‘ descritto dal diagramma temporale
di fig. 8-19. In esso la fase denominata “eliminazione dei conflitti” comprende il
presvuotamento di tutti i processi che girano nel nucleo ed il rilascio, da parte dei processi
meno prioritari, di tutte le risorse richieste dal processo piu’ prioritario.

evento risposta all’evento

elaborazione dispatch latency


della esecuzione
interruzione del processo
in tempo reale

commutazione
eliminazione al nuovo
dei processo
conflitti (context switch)

tempo di risposta

tempo

Fig. 8-19 – Componenti del tempo di risposta nella schedulazione in tempo reale

Capitolo 8 21
8.11 - STALLO

8.11.1 - MODELLO DEL SISTEMA DI ELABORAZIONE

La condizione di stallo (“deadlock”) si presenta allorche’ uno o piu’ processi sono nello stato
WAITING in quanto in attesa di ricevere risorse assegnate ad altri processi a loro volta nello
stato WAITING in quanto in attesa di ricevere risorse assegnate ai primi. Per studiare il
problema si ricorre ad un modello del sistema di elaborazione che tiene conto dei tipi di
risorse e del numero di copie di ciascun tipo. Vi sono tipi di risorse fisiche (per es., spazio di
memoria, cicli di CPU, unita’ a nastro magnetico, stampanti) e tipi di risorse logiche (per es.,
file, monitor). Se di una risorsa vi sono piu’ copie, esse devono essere considerate del tutto
equivalenti e ad un processo che richiede la risorsa se ne puo’ assegnare una qualunque
copia. Un processo puo’ richiedere tutti i tipi di risorse che gli servono per completare
l’esecuzione, ovviamente per ciascun tipo non in numero superiore alle copie disponibili.

In condizioni normali di funzionamento l’impiego di una risorsa da parte di un processo


comporta le tre seguenti attivita’:
1. richiesta (con eventuale attesa se la richiesta non e’ immediatamente soddisfacibile);
2. uso;
3. rilascio.
Richiesta e rilascio sono attuate attraverso chiamate al sistema (del tipo request device,
release device, open file, close file, allocate memory, free memory). Il S.O. si deve far
carico di mantenere aggiornata una tabella che tiene traccia delle varie risorse e di quante di
esse sono libere od assegnate ed in questo caso a quale processo; nel caso poi di un
processo che richiede una risorsa gia’ assegnata ad un altro processo, il S.O. deve gestire le
code di processi in attesa delle varie risorse.

Situazioni di stallo si possono verificare sia per processi che richiedono uno stesso tipo di
risorsa, sia per processi che richiedono risorse di tipi diversi.

8.11.2 - CONDIZIONI PER LO STALLO

Affinche’ si verifichi uno stallo devono essere soddisfatte le seguenti quattro condizioni:

1. Mutua esclusione
Deve esistere almeno una risorsa posseduta dai processi in modo non condivisibile.
2. Possesso ed attesa
Deve essere presente un processo che ha ottenuto almeno una risorsa e che e’ in
attesa di ulteriori risorse che sono assegnate ad altri processi.
3. Assenza di diritto di prelazione
Una risorsa assegnata ad un processo puo’ essere rilasciata solo volontariamente dal
processo stesso.
4. Attesa circolare
Deve esistere un insieme di processi in attesa {P0 , P1 , ...,Pn} in cui P0 e’ in attesa di
una risorsa assegnata a P1, P1 e’ in attesa di una risorsa assegnata a P2, e cosi’ via
sino a Pn che e’ in attesa di una risorsa assegnata a P0.

Le quattro condizioni non sono totalmente indipendenti ma e’ in ogni caso comodo prenderle
tutte in esame.

8.11.3 - GRAFO DI ALLOCAZIONE DELLE RISORSE

Questo grafo orientato consiste di un insieme di vertici V, suddiviso in due sottoinsiemi


disgiunti (P, costituito dai processi attivi nel sistema; R, costituito da tutti i tipi di risorse
Capitolo 8 22
presenti nel sistema), e di un insieme E di archi che congiungono un vertice di P ed un
vertice di R.

Graficamente i processi sono rappresentati da cerchi e le risorse da rettangoli all’interno dei


quali compaiono tanti cerchietti quante sono le copie disponibili della risorsa.
Un arco orientato dal cerchio Pi al rettangolo Rj indica che il processo Pi ha richiesto una
risorsa del tipo Rj ed e’ in attesa di tale risorsa (arco di richiesta). Un arco orientato da uno
dei cerchietti di una risorsa Rj ad un cerchio Pi indica che una risorsa di tipo Rj e’ stata
assegnata al processo Pi (arco di assegnazione).
Allorche’ un processo richiede una risorsa, si inserisce un arco di richiesta nel grafo: se la
richiesta puo’ essere soddisfatta, l’arco e’ trasformato in un arco di assegnazione.
Naturalmente quando un processo non usa piu’ una risorsa, l’arco di assegnazione e’
rimosso.
La fig. 8-20 riporta un esempio di grafo di allocazione delle risorse.

R1 R3

P1 P2 P3

R2
R4

Fig. 8-20. Esempio di grafo di allocazione delle risorse

Se un grafo di allocazione delle risorse non contiene cicli si puo’ dimostrare che non vi sono
processi in condizione di stallo.
Se un grafo contiene un ciclo, si ha stallo se il ciclo coinvolge solo risorse delle quali esiste
una sola copia nel sistema (in effetti sono in condizione di stallo tutti i processi che sono
coinvolti nel ciclo) mentre se il ciclo coinvolge risorse delle quali esistono piu’ copie non e’
detto che si sia verificato uno stallo.
Se, per es., a partire dalla situazione di fig. 8-20 si aggiunge la richiesta da parte di P3 di
una copia della risorsa R2, nel grafo di allocazione risultante (fig. 8-21) vi sono due cicli e
tutti e tre i processi sono in situazione di stallo. Infatti P2 e’ in attesa di R3 che e’ stata
assegnata a P3 , P3 e’ in attesa che P1 o P2 rilascino R2 ed infine P1 e’ in attesa di R1
assegnata a P2 .

Capitolo 8 23
R1 R3

P1 P2 P3

R2
R4

Fig. 8-21. Grafo di allocazione delle risorse con stallo

Nel grafo di allocazione di fig. 8-22 e’ pure presente un ciclo ma non vi e’ stallo: e’ sufficiente
infatti che, per esempio, il processo P4 rilasci la sua copia di R2 perche’ questa risorsa venga
assegnata a P3 spezzando il ciclo.

R1 P2

P1 P3

R2 P4

Fig. 8-22. Grafo di allocazione delle risorse con ciclo ma senza stallo

8.11.4 - METODI PER FRONTEGGIARE GLI STALLI

Va detto, innanzi tutto, che alcuni S.O. adottano l’approccio di ignorare il problema alla luce
della bassa frequenza con la quale si presenta uno stallo (per es. una volta all’anno) e del
costo degli altri approcci, ma il numero di S.O. che sono in grado di fronteggiare lo stallo sta
crescendo in quanto aumentano i numeri di processi, di processori ed in generale di risorse
presenti. D’altra parte se un sistema e’ nella condizione di stallo e questa situazione non
viene rilevata si ha un degrado delle prestazioni perche’ vi sono risorse assegnate a processi
che non possono procedere con l’esecuzione.

Se invece si vuole intervenire per fronteggiare un eventuale stallo i S.O. usano metodi
riconducibili a due approcci:
(a) adottare un protocollo che garantisca che il sistema non entrera’ mai in uno stato con
stallo; al riguardo vi sono due alternative, una che mira a prevenire il verificarsi dello stallo
(“deadlock prevention”) ed una che mira ad evitare lo stallo (“deadlock avoidance”);
(b) consentire al sistema di entrare in uno stato di stallo per poi ricondurlo ad uno stato
senza stallo.

Capitolo 8 24
8.11.5 - COME PREVENIRE LO STALLO

Per prevenire il verificarsi di uno stallo e’ sufficiente fare in modo che non sia soddisfatta
almeno una delle quattro condizioni necessarie per lo stallo, imponendo vincoli sulle
modalita’ con le quali si possono avanzare richieste di risorse.

La condizione di mutua esclusione si applica alle risorse non condivisibili ed in generale non
e’ possibile violarla in quanto alcune risorse (per es. le stampanti) sono intrinsecamente non
condivisibili.

Per far si’ che non si presenti mai la condizione di possesso ed attesa occorre fare in modo
che, quando un processo richiede una risorsa, non sia in possesso di altre risorse.
Questo risultato si puo’ ottenere in due modi: il primo prevede che un processo richieda e
riceva tutte le risorse di cui ha bisogno prima di cominciare l’esecuzione; il secondo prevede
che un processo possa richiedere risorse solo quando non ne ha. Nel primo caso basta
prevedere che tutte le chiamate a sistema che richiedono risorse precedano tutte le altre
chiamate a sistema; nel secondo caso un processo puo’ richiedere risorse ed usarle ma
prima di poter chiedere altre risorse deve rilasciare quelle che gli erano state assegnate.
Questi approcci presentano due grossi inconvenienti: scarsa utilizzazione delle risorse,
morte per fame. Infatti nel primo modo le risorse possono essere assegnate ma non utilizzate
per un lungo intervallo di tempo; ma anche nel secondo modo puo’ accadere lo stesso se
non si e’ sicuri di ritrovare le risorse (per es. un disco) nello stato in cui erano state
rilasciate. Inoltre un processo che ha bisogno di diverse risorse molto usate puo’ essere
obbligato ad attendere indefinitamente perche’ una delle risorse di cui ha bisogno e’ sempre
assegnata a qualche altro processo.

Per fare in modo che non sia soddisfatta la terza condizione, l’assenza di diritto di
prelazione, si puo’ seguire il seguente procedimento. Se un processo al quale sono state
assegnate risorse richiede ulteriori risorse che non gli possono essere assegnate subito,
viene privato di tutte le risorse che sono aggiunte alla lista di risorse delle quali il processo e’
in attesa. Il processo in questione riprendera’ l’esecuzione solo quando potra’ ricevere sia le
risorse che gia’ aveva sia le nuove richieste.
In alternativa si puo’ procedere nel seguente modo. Allorche’ un processo richiede nuove
risorse, se esse non sono disponibili si controlla se per caso sono assegnate a processi che
sono a loro volta in attesa di risorse aggiuntive: in tal caso le risorse vengono tolte ai
processi in attesa ed assegnate al processo che ha fatto la richiesta; se invece non sono
disponibili ma non sono assegnate a processi in attesa il processo richiedente deve
attendere ed eventualmente puo’ perdere nel frattempo qualcuna delle sue risorse se
vengono richieste da altri processi. Un processo puo’ ripartire solo quando gli sono
assegnate le risorse che chiede ed ha ricuperato tutte le risorse che eventualmente gli sono
state tolte durante l’attesa.

Per fare in modo che la condizione di attesa circolare non sia soddisfatta si dimostra che e’
sufficiente imporre un ordinamento totale di tutti i tipi di risorse ed esigere che ogni processo
richieda le risorse in ordine crescente di elencazione.
Se R e’ l’insieme dei tipi di risorse, a ciascun tipo si assegna un diverso numero intero (cioe’
si definisce una funzione F uno-ad-uno da R all’insieme dei numeri naturali N) in modo da
poter confrontare due risorse e stabilire se una precede l’altra. Sulla base delle indicazioni
sperimentali la funzione F potrebbe essere definita per esempio nel seguente modo:
F(nastro magnetico) = 1
F(disco) = 5
F(stampante) = 12
A questo punto il procedimento da seguire per impedire il soddisfacimento della condizione
e’ il seguente. Ogni processo puo’ richiedere risorse solo secondo l’ordine di numerazione
crescente. In altri termini un processo all’inizio puo’ richiedere qualsiasi numero di copie di
una risorsa Ri , dopo di che puo’ richiedere copie di una risorsa Rj see

Capitolo 8 25
F(Rj ) > F(Ri )
Se sono richieste piu’ copie di una stessa risorsa e’ necessario formulare una sola richiesta.
In alternativa si puo’ stabilire che, ogni volta che un processo richiede una copia di una
risorsa Rj , esso abbia rilasciato tutte le risorse tali che F(Ri ) >= F(Rj).

8.11.6 - COME EVITARE LO STALLO

Per non incorrere nello stallo anziche’ imporre restrizioni su come le richieste possono
essere fatte si possono richiedere maggiori informazioni sulle sequenze di richieste e di
rilasci di risorse da parte dei vari processi, in modo da poter decidere di volta in volta se una
richiesta puo’ essere evasa o se deve attendere.

Algoritmo basato sul numero massimo di risorse richieste


Un primo approccio e’ basato sulla conoscenza del numero massimo di risorse di ciascun
tipo richieste dai vari processi. Da questo punto di vista si dice che un sistema e’ in uno
stato sicuro se esiste una sequenza di processi <P0 , P1 , ...., Pn > tale che, per ogni Pi , le
risorse aggiuntive che Pi puo’ ancora richiedere possono essere ottenute dalle risorse al
momento libere e da quelle assegnate ai processi Pj con j<i. In questo modo tutti i processi
possono ordinatamente completare l’esecuzione. Lo stato di un sistema e’ detto invece stato
insicuro se non esiste una siffatta sequenza.
Il diagramma degli stati di un sistema si presenta come in fig. 8-23. Si noti che uno stato di
stallo e’ uno stato insicuro ma non viceversa; tuttavia uno stato insicuro puo’ portare ad
uno stallo a seguito delle richieste avanzate dai processi.

NON SICURI
STALLO

SICURI

Fig. 8-23. Spazio degli stati di un sistema

Si consideri, per esempio, un sistema avente 12 unita’ a nastro magnetico e 3 processi P0,
P1, P2 caratterizzati dai dati della tabella, che riporta anche le assegnazioni all’istante t0
(risultano quindi libere 3 unita’).

Processo Richieste Assegnazioni


massime correnti
P0 10 5
P1 4 2
P2 9 2

Il sistema e’ in uno stato sicuro grazie alla sequenza <P1 ,P0 ,P2 >. Tuttavia esso puo’
evolvere verso uno stato insicuro se, per es., all’istante t1 il processo P2 richiede e riceve una
ulteriore unita’. Infatti il processo P1 puo’ arrivare in fondo ma le 4 unita’ che libera, se P0 e
P2 dovessero richiederne il numero massimo indicato, non consentono il completamento ne’
di P0 ne’ di P2 , che vanno in attesa l’uno delle risorse dell’altro ed originano uno stallo.

Per applicare questo principio occorre fare in modo che il sistema, che ovviamente parte da
uno stato sicuro, resti sempre in uno stato sicuro: pertanto di fronte ad una richiesta di
Capitolo 8 26
risorse disponibili il sistema decide di procedere alla allocazione solo se resta in uno stato
sicuro.

Algoritmo basato sul grafo di allocazione delle risorse


Questo algoritmo si applica solo per risorse disponibili in copia unica e si basa sul seguente
principio: la richiesta di una risorsa Rj da parte di un processo Pi puo’ essere accolta solo se
non conduce alla formazione di un ciclo nel grafo di assegnazione delle risorse: infatti in tal
caso il sistema resta in uno stato sicuro. Se invece si forma un ciclo, il processo deve
attendere.

Si consideri per es. il grafo di fig. 8-24a. La risorsa R2 e’ attualmente libera ma non puo’
essere assegnata a P2 in quanto si viene a creare un ciclo (fig. 8-24b). Se a questo punto P1
richiede R2 e P2 richiede R1 si ha uno stallo.

R1
(a)

P1 P2

R2

R1
(b)

P1 P2

R2

Fig. 8-24. Uso del grafo di allocazione per evitare lo stallo

Algoritmo del banchiere


Questo algoritmo si applica anche a sistemi con risorse disponibili in piu’ copie ed e’
denominato cosi’ perche’ puo’ essere usato da una banca per gestire le linee di credito dei
clienti tenendo conto delle disponibilita’ della banca.
Esso si basa sull’uso di:
• tre matrici che hanno tante righe quanti sono i processi presenti e tante colonne quanti
sono i tipi di risorse utilizzati: la matrice Risorse allocate (ALL) riporta il numero di
risorse di ciascun tipo assegnate ai vari processi, la matrice Risorse massime richieste
(MAX) riporta le richieste massime dei processi, la matrice Risorse necessarie (NEC)
riporta il numero di risorse ancora da acquisire dai processi (ovviamente NEC=MAX-ALL);
• tre vettori che hanno tante componenti quanti sono i tipi di risorse da gestire: i valori
delle componenti del vettore ESIST indicano i numeri di risorse esistenti nel sistema,
quelli del vettore ASS indicano i numeri di risorse assegnate ai processi, quelli del vettore
DISP indicano i numeri di risorse ancora disponibili (ovviamente DISP=ESIST-ASS).

L’algoritmo per verificare se uno stato e’ sicuro consta di tre passi:


1. Si cerca nella matrice NEC un processo le cui richieste ancora da soddisfare sono
ordinatamente tutte minori delle componenti di DISP: se non si trova alcun processo, il
sistema e’ in stato di stallo, dato che nessun processo puo’ arrivare a completarsi;

Capitolo 8 27
2. Si immagina che il processo individuato in 1. riceva le risorse di cui ha ancora bisogno e
si completi: il processo viene segnato come completato e si determina il nuovo vettore
DISP sommando alle risorse precedentemente disponibili tutte quelle possedute dal
processo che può completarsi;
3. Si ripetono i passi 1. e 2. sino a che o tutti i processi sono segnati come terminati, nel
qual caso lo stato di partenza era sicuro, o si arriva ad uno stallo, nel qual caso lo stato di
partenza era insicuro.

Val la pena di notare che questo algoritmo ha una applicabilita’ limitata sia perche’
difficilmente i processi conoscono le proprie esigenze massime di risorse, sia perche’
l’insieme di processi da gestire cambia nel tempo, sia infine perche’ delle risorse possono
diventare non utilizzabili (per es. per guasti).

Si consideri il seguente esempio. Un sistema deve gestire 5 processi che utilizzano 3 tipi di
risorse, A, B, e C. Le matrici ALL, MAX e NEC ed il vettore DISP sono riportati in fig. 8-25
(SITUAZIONE A); gli altri due vettori risultano ESIST=(10, 5, 7) e ASS=(7, 2, 5).
Si puo’ verificare che lo stato e’ sicuro, per es. esaminando i processi nell’ordine P1, P3, P4,
P2, P0.

Supponiamo che il processo P1 avanzi la richiesta di 1 risorsa di tipo A e di 2 risorse di tipo


C. Supponendo di accogliere la richiesta si ottiene la situazione sintetizzata in fig. 8-25
(SITUAZIONE B). Si puo’ verificare che lo stato e’ sicuro, per es. esaminando i processi
nell’ordine P1, P3, P0 , P4, P2 e pertanto la richiesta puo’ essere soddisfatta subito. Una
successiva richiesta di 2 unita’ di tipo B da parte di P0 non puo’ invece essere accolta in
quanto conduce ad uno stato insicuro.

8.11.7 - RILEVAZIONE DELLO STALLO

Se un sistema non impiega nessuno dei metodi prima descritti per prevenire od evitare lo
stallo, uno stallo puo’ verificarsi e se il problema non viene ignorato il sistema deve
prevedere sia un procedimento per esaminare lo stato e rilevare se vi e’ stallo, sia un
procedimento per eliminare lo stallo.

Per rilevare lo stallo nel caso in cui esiste una sola copia di tutte le risorse si puo’ usare una
variante del grafo di allocazione delle risorse denominata grafo delle attese, che si ottiene dal
primo rimuovendo i nodi che individuano i tipi di risorse e unificando gli archi. In questo
nuovo grafo un arco che collega un processo Pi ad un processo Pj sta ad indicare che il
processo Pi e’ in attesa che il processo Pj rilasci una risorsa della quale Pi ha bisogno.
Naturalmente esiste un arco tra due processi solo se il corrispondente grafo di allocazione
delle risorse contiene, per una specifica risorsa Rq, sia l’arco tra Pi ed Rq sia l’arco tra Rq e Pj.
Si ha uno stallo se e solo se nel grafo delle attese e’ presente un ciclo.
La fig. 8-26 riporta un grafo di allocazione delle risorse ed il corrispondente grafo delle
attese.

Capitolo 8 28
SITUAZIONE A

Processi Risorse Risorse


allocate max richieste ancora disponibili

A B C A B C A B C
P0 0 1 0 7 5 3 3 3 2
P1 2 0 0 3 2 2
P2 3 0 2 9 0 2
P3 2 1 1 2 2 2
P4 0 0 2 4 3 3

Processi Risorse
necessarie
A B C
P0 7 4 3
P1 1 2 2
P2 6 0 0
P3 0 1 1
P4 4 3 1

SITUAZIONE B

Processi Risorse Risorse


allocate necessarie ancora disponibili
A B C A B C A B C
P0 0 1 0 7 4 3 2 3 0
P1 3 0 2 0 2 0
P2 3 0 2 6 0 0
P3 2 1 1 0 1 1
P4 0 0 2 4 3 1

Fig. 8-25 – Esempi di applicazione dell’algoritmo del banchiere

Capitolo 8 29
P5

R1 R4
R3

P1 P2 P3

P4

R2 R5

Fig. 8-26a. Grafo di allocazione delle risorse

P5

P1 P2 P3

P4

Fig. 8-26b. Grafo delle attese corrispondente al grafo di fig. 8-26a

Nel caso invece di risorse con molteplici copie si puo’ usare l’algoritmo del banchiere.
Come ulteriore esempio della sua utilizzazione si consideri la situazione illustrata in fig. 8-
27a. Con riferimento alla terminologia prima introdotta si ha ESIST=(7, 2, 6)=ASS. Nello
stato descritto non si ha stallo: basta considerare per esempio la sequenza P0, P2, P3, P1, P4.
Se la matrice NEC viene modificata come indicato in fig. 8-27b si ha invece stallo: anche se
si ricuperano le risorse di P0, esse non sono sufficienti per soddisfare le richieste degli altri
processi.

Capitolo 8 30
CASO A: SITUAZIONE SENZA PERICOLO DI STALLO

Processi Risorse Risorse


allocate necessarie ancora disponibili
A B C A B C A B C
P0 0 1 0 0 0 0 0 0 0
P1 2 0 0 2 0 2
P2 3 0 3 0 0 0
P3 2 1 1 1 0 0
P4 0 0 2 0 0 2

CASO B: SITUAZIONE CON PERICOLO DI STALLO

Processi Risorse Risorse


allocate necessarie ancora disponibili
A B C A B C A B C
P0 0 1 0 0 0 0 0 0 0
P1 2 0 0 2 0 2
P2 3 0 3 0 0 1
P3 2 1 1 1 0 0
P4 0 0 2 0 0 2

Fig. 8-27. Ulteriore esempio di applicazione dell’algoritmo del banchiere

Data l’onerosita’ di applicazione dell’algoritmo di rilevazione dello stallo in pratica si puo’


pensare di applicarlo con bassa frequenza, per esempio una volta all’ora, o quando
l’utilizzazione della CPU scende al di sotto di livelli prestabiliti, per esempio il 40%.

8.11.8 - ELIMINAZIONE DELLO STALLO

Una volta che e’ stato determinato che esiste uno stallo, si puo’ affidare il compito di
eliminarlo all’operatore del sistema oppure si puo’ procedere automaticamente: in questo
secondo caso si puo’ forzare la terminazione di uno o piu’ processi oppure si puo’ applicare il
presvuotamento di risorse per qualcuno dei processi.

Nell’eliminazione di uno stallo con la forzata terminazione di uno o piu’ processi si puo’
procedere o facendo abortire tutti i processi che si trovano in condizione di stallo oppure
facendo abortire un processo alla volta sino ad eliminare lo stallo. In entrambi i casi vi sono
degli inconvenienti: nel primo caso si rischia di buttar via un cospicuo volume di
elaborazione gia’ effettuato, nel secondo caso l’algoritmo per scoprire se esiste uno stallo va
riapplicato dopo ogni rimozione. In ogni caso non e’ facile far abortire un processo: si pensi
ad un processo che sta aggiornando un file o ad uno che sta stampando dei risultati.
Nel caso di terminazione graduale non e’ poi facile determinare da quale processo partire.
Teoricamente si dovrebbe partire dal processo la cui terminazione comporta il costo minimo,
ma non e’ facile stabilire il costo di terminazione dei processi: molto spesso si ricorre
pertanto, per semplicita’, a considerazioni quali per es. la priorita’, la semplicita’ di
presvuotamento delle risorse assegnate ai processi, la durata di esecuzione gia’ svolta.

Nella eliminazione dello stallo con l’introduzione del diritto di prelazione si sottraggono
risorse ad alcuni processi e si attribuiscono ad altri sino a rimuovere la condizione di stallo.
Anche in questo caso non e’ facile procedere. Innanzi tutto occorre stabilire da quale
processo cominciare, cercando di minimizzare il costo del presvuotamento. In secondo
luogo, occorre stabilire cosa fare del processo al quale si tolgono risorse: certamente non
Capitolo 8 31
puo’ proseguire e dovrebbe essere riportato indietro ad uno stato sicuro, da cui poi ripartire.
Pero’ per riportarlo ad uno stato sicuro occorre che il S.O. tenga traccia dello stato dei
processi in esecuzione: se cosi’ non e’, si deve far ripartire dall’inizio e cioe’ si deve far
abortire. In terzo luogo occorre evitare di provocare la “morte per fame” di un processo, per
es. fissando un tetto al numero di presvuotamenti che un processo puo’ subire.

8.11.9 - APPROCCIO COMBINATO ALLO STALLO

Per ottenere migliori risultati si puo’ pensare di combinare gli approcci prima descritti nel
seguente modo. Le risorse del sistema sono suddivise in classi ordinate gerarchicamente e
all’interno di ciascuna classe si applica la tecnica piu’ appropriata per gestire lo stallo. Il
ricorso all’ordinamento fa si’ che uno stallo non possa mai coinvolgere piu’ di una classe.

Si consideri, come es. di applicazione di questo metodo, la seguente suddivisione in classi e


la corrispondente scelta di algoritmi:
Classe 1: risorse interne
Si tratta delle risorse usate dal S.O., per es. i P.C.B.; per queste si puo’ applicare la
prevenzione dello stallo attraverso l’ordinamento delle risorse;
Classe 2: memoria primaria
Si tratta della memoria usata da un processo; si puo’ ricorrere alla prevenzione attraverso il
presvuotamento, che non pone problemi potendosi sempre trasferire il contenuto della Mp
su memoria secondaria (“area di swap”);
Classe 3: Risorse del job
Si tratta dei dispositivi che possono essere assegnati ai job ed ai processi ai quali danno
origine e dei file usati dai job; in questo caso lo stallo puo’ essere evitato per mezzo
dell’algoritmo del banchiere dato che le schede controllo del job danno indicazioni sulle
risorse richieste;
Classe 4: Spazio per lo “swap”
Si tratta dello spazio riservato ai processi per compiere operazioni di “swap” sul supporto di
memorizzazione secondario; in questo caso si puo’ usare la tecnica della preallocazione dato
che solitamente e’ noto il massimo spazio necessario.

Capitolo 8 32