You are on page 1of 4

La sincronizzazione tra processi

Nei programmi concorrenti sono possibili gli errori legati ai tempi di esecuzione e di
schedulazione nella CPU che non si possono determinare ed eliminare tramite testing ma
devono essere eliminati tramite una programmazione particolarmente accurata, individuando
"dove il codice" potrebbe essere causa di possibili situazioni di errore, che quasi sempre è
connesso con la condivisione e la comunicazione di dati tra processi concorrenti.

Abbiamo due vincoli da soddisfare:

1. determinismo: i risultati devono essere uguali per ogni esecuzione,


indipendentemente dalla sequenza di schedulazione;
2. timing constraint: i risultati devono essere prodotti entro certi limiti temporali fissati.

Gli errori dipendenti dal tempo sono causati da una scorretta sincronizzazione dei processi.
Le caratteristiche degli errori dipendenti dal tempo sono:

● irriproducibili: possono verificarsi con alcune sequenze e non con altre;


● indeterminati: esito ed effetti dipendono dalla sequenza;
● latenti: possono presentarsi solo con sequenze rare;
● difficili da verificare e testare: perché le tecniche di verifica e testing si basano sulla
riproducibilità del comportamento.

Se una risorsa è allocata come dedicata non è necessaria la sincronizzazione mentre se


una risorsa è condivisa è necessario assicurare che gli accessi avvengano in modo non
divisibile, quindi in mutua esclusione.

Interleaving e Overlapping
Nella macchina con un singolo processore i processi sono "alternati nel tempo" ma con
velocità tali da dare impressione di avere un multiprocessore: in questo caso si definisce
interleaving la situazione di alternanza casuale.

Nei sistemi multiprocessore più processi vengono eseguiti simultaneamente su processori


diversi e quindi sono "sovrapposti nel tempo": in questo caso si definisce overlapping la
sovrapposizione temporale di processi.

Potrebbe anche esserci una situazione in cui sono presenti più macchine, ciascuna
multitasking e quindi avere una combinazione delle due situazioni sopra descritte.

Condizioni di Bernstein
Una o più aree di memoria si definisce come dominio di una istruzione o procedura, mentre
il contenuto di una o più aree di memoria si definisce come rango di una istruzione o
procedura.
I vincoli che devono soddisfare due istruzioni per essere eseguite concorrentemente sono le
condizioni di Bernstein, e le condizioni sono:

Se due (o più) istruzioni soddisfano le condizioni di Bernstein il risultato è indipendente dalla


particolare sequenza di esecuzione eseguita dai processori (interleaving) e sarà quindi
identico alla loro esecuzione seriale.
Quando anche una sola condizione viene violata si ottengono errori generati dal tempo
dovuti al fenomeno dell'interferenza.

Starvation e deadlock
Un errata sincronizzazione può portare al fallimento delle elaborazioni, che genera situazioni
di:

● starvation (o blocco individuale): si verifica quando un processo rimane in attesa di


un evento che non accadrà mai, e quindi non può portare a termine il proprio lavoro.
● deadlock (blocco multiplo): si verifica quando due o più processi rimangono in attesa
di eventi che non potranno mai verificarsi a causa di condizioni cicliche nel possesso
e nella richiesta di risorse.

Proprietà
Abbiamo tre diverse proprietà:

● safety: i processi non devono "interferire" tra loro nell'accesso alle risorse condivise e
i meccanismi di sincronizzazione servono a garantire la proprietà di safety;
○ Tra le molte possibili cause di errore e interferenza tra processi che possono
violare la safety ricordiamo:
○ • la presenza di corse critiche causate da una non corretta mutua esclusione;
○ • l’accesso e le azioni su stati di risorse non consistenti;
○ • la violazioni dell’atomicità di certe operazioni (lock e P() su semafori);
○ • esecuzione di azioni che dovrebbe essere proibite ma permesse dal
linguaggio di programmazione utilizzato
○ per scrivere i programmi;
○ • esecuzione di operazioni su valori e dati non aggiornati, mantenuti in cache
e quindi non consistenti.

● fairness: il SO deve sempre mandare in esecuzione qualsiasi processo e le richieste
di esecuzione di un processo devono essere prima o poi soddisfatte; in questo modo
viene garantita l'assenza di starvation.
● liveness: deve garantire che il processo che avanza porterà a termine in modo
corretto il proprio lavoro; è la proprietà che esclude situazioni di deadlock.
○ Tra le molte possibili cause di errore e interferenza tra processi che possono
violare la liveness ricordiamo:
○ • errori di sincronizzazione;
○ • errori con i segnali tra processi cooperanti;
○ • fallimenti di un processo: si attende un segnale da un altro processo che è
andato in crash;
○ • livelock: continuo tentativo di una azione che fallirà sempre;
○ • lockout: chiamata di una operazione che non sarà mai disponibile;
○ • esaurimento di una risorsa necessaria per evolvere.

SPIN LOCK

Problema della indivisibilità

Per come abbiamo scritto l’istruzione di lock() non possiamo garantire l’acceso seriale a una
risorsa, in quanto potrebbe verificarsi una situazione di interleaving indesiderata.

Vediamo, per esempio, la seguente situazione:

1 ipotizziamo che il semaforo sia verde x = 1;

2 il processo P1 effettua la lock(x) fino a verificare la condizione di test ma viene sospeso


prima che ne possa modificare il valore;

3 un secondo processo P2 effettua anch’esso la lock(x) e, trovando il semaforo verde, lo


mette a rosso e inizia a utilizzare la risorsa;

4 quando viene risvegliato il processo P1 esegue l’istruzione che pone a rosso il semaforo
(che di fatto però è già rosso) e anch’esso utilizza la risorsa.

funzione lock(x)
inizia
ripeti // ciclo di attesa sul semaforo nel caso che sia rosso
<ritardo>
finché TestAndSet(x) // esci dal ciclo settando il semaforo a rosso
fine

funzione unlock(x)
inizia
set(x); // metti il semaforo a verde
fine
funzione P(S)
inizia
<disabilita le interruzioni>
LOCK(SX)
se S=0
allora
<poni il processo nella coda di attesa>
altrimenti
S ĸ S-1; // accedi alla risorsa
UNLOCK(SX)
<abilita le interruzioni>
fine

funzione V(S)
inizia
<disabilita le interruzioni>
LOCK(SX)
se <è presente un processo in attesa>
allora
<poni il primo processo nello stato di pronto>
S ĸ S+1; // rilascia la risorsa
UNLOCK(SX)
<abilita le interruzioni>
fine

You might also like