Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
La Mutua Esclusione riguarda le situazioni per cui bisogna impedire che due o pi processi
operino contemporaneamente sui dati condivisi.
Possibili soluzioni: soluzioni software (variabili di blocco), soluzioni hardware (istruzioni
atomiche), primitive di nucleo (ad esempio semafori), soluzioni linguistiche (monitor).
Possibili pseudocodici delle soluzioni software sono :
processo1{
while (true){
Sezione_noncritica;
while(turno==1);
Sezione critica1;
turno=1;
}
}
processo2{
while (true){
Sezione_noncritica;
while(turno==2);
Sezione critica2;
turno=2;
}
}
e lalgoritmo di Peterson:
processo1{
while (true){
Sezione_noncritica;
a=0; t=1;
while(b==0 &&
t==1);
Sezione critica1;
a=1;
}
}
processo2{
while (true){
Sezione_noncritica;
b=0; t=2;
while(a==0 &&
t==2);
Sezione critica2;
b=1;
}
}
(1.2)
T2
T1
T3
T4
T5
T6
z+t
cobegin
T1;
T2;
coend;
cobegin
T3;
T4;
coend;
cobegin
T5;
T6;
coend;
return(z+t);
}
Usando invece un linguaggio che realizza la concorrenza con le primitive fork/join, si pu
realizzare il seguente programma concorrente:
float calcola(a,b,c,d,e,f)
{
P1=fork(T1);
T2;
P2=fork(T4);
T3;
Join(P1);
T5;
Join(P2);
T6;
Return(z+t):
}
3) Si supponga di essere in un ambiente a memoria condivisa. Scrivere uno pseudocodice del
produttore/consumatore usando lalgoritmo di Peterson per la mutua esclusione e due semafori
per le sincronizzazioni. Quali sono le variabili da condividere?
Il codice, usando semafori e lalgoritmo di Peterson, potrebbe essere scritto in pseudo-C in questo
modo:
produttore(){
while(true){
p=0; t=1;
elto=produci();
down(pieno);
while(q!=1 && t==1) ;
*ptr++=elto ;
p=1 ;
up(vuoto) ;
}
}
consumatore(){
while(true){
q=0; t=2;
down(vuoto);
while(p!=1 && t==2) ;
elto=*ptr-- ;
q=1 ;
up(pieno) ;
}
}
Questo pseudo-codice usa un buffer condiviso (puntato da ptr, puntatore condiviso), due semafori
per la sincronizzazione del buffer, pieno e vuoto, e tre variabili condivise, p, q e t. per
lalgoritmo di Peterson.
4) i supponga che una architettura di un calcolatore offra al programmatore una istruzione atomica
scambia(a,b), che realizza la seguente funzione:
scambia(short &a, short &b){
short t;
t = *a; *a = *b; *b = t;
}
Come si puo utilizzare questa istruzione per proteggere una sezione critica ?
(1.4)
La mutua esclusione si pu ottenere usando la procedura atomica scambia nel seguente modo:
a=0: //variabile globale
processo1{
b=1;
while(true) {
while(b==1) scambia(a,b);
Sezione_critica1();
scambia(a,b);
}
}
5)
processo2{
c=1;
while(true) {
while(c==1) scambia(a,c);
Sezione_critica1();
scambia(a,c);
}
}
Come si pu risolvere questo problema usando i monitor di java? Modificare il programma per
questo scopo.
Un modo per risolvere il problema pu essere di definire una classe condivisa, chiamata diciamo S,
come segue:
public class S{
private int i;
S(){i=0;}
synchronized
i=1;
}
synchronized
i=2;
}
void s1(){
System.out.println(Sono il thread scrivi1: i=+i);
void s2(){
System.out.println(Sono il thread scrivi2: i=+i);
6) Si supponga di avere un programma Java, costituito da due threads (riportati in seguito) che
vengono attivati in concorrenza dal programma principale. I due thread condividono la variabile
i. Si vuole che il programma produca una sequenza di numeri uguali, in particolare 5 5 5 5
public class Inc extends Thread{
public void run(){
while(true){
System.out.println(i=+i);
i++;
}
}
}
a) Mostrare un interleaving tale che i due thread non producono la sequenza desiderata..
b) Modificare il programma usando alcuni semafori per avere luscita desiderata ed indicando il
valore iniziale degli stessi.
Inizializzando la variabile i a 5, si pu immaginare ad esempio la seguente schedulazione:
public class Inc()extends Threads{
public void run(){
while(true){
System.out.println(i=+i);
i++;
}
}
}
cio, il primo thread inizia, stampa 5, poi passa al secondo, che stampa 5 e ritorna al primo, che
incrementa i che passa a 6 e stampa 6, poi passa al secondo che stampa 6 e decrementa etc. La
sequenza stampata pertanto: 556655
Una possibile soluzione , tralasciando le definizioni dei semafori:
public class Inc()extends Threads{
public void run(){
while(true){
mutex.down();
s1.down():
System.out.println(i=+i);
s2.up();
s3.down();
i++;
mutex.up();
}
}
}
public class Dec()extends Threads{
public void run(){
while(true){
s2.down();
System.out.println(i=+i);
i--;
s3.up();
s1.up():
}
}
}
8) Si supponga che un Sistema Operativo con 5 processi concorrenti si trovi nella seguente
situazione, nei confronti delle risorse:
(1)
4 tipi di risorse. Capacit del sistema: |4 4 4 4|
Risorse allocate nel sistema:
1
0
0
1
0
0
1
1
1
0
0
1
1
0
1
1
0
0
0
1
Risorse richieste:
3
4
3
2
2
3
2
2
1
3
2
2
1
1
2
1
3
1
2
2
Il sistema in uno stato sicuro o no? Nel caso che sia in uno stato non sicuro, cosa si puo fare per
risolvere il problema?
Per vedere se uno stato sicuro, possiamo vedere se le richieste possono essere soddisfatte.
Intanto possiamo vedere che le risorse disponibili sono: V=|2 1 1 2|.
Possiamo allora soddisfare le richieste del 4 processo che, quando termina rilascia le sue risorse
e quindi le risorse disponibili sono: V=|3 2 1 2|. Possiamo allora eseguire il 3 processo. Dopo la
terminazione, si arriva ad avere V=!3 3 2 2|. Possiamo allora eseguire il primo processo,
arrivando ad avere alla sua terminazione: V=|4 3 2 3|. Possiamo eseguire il secondo, con V=|4 4 3
3| ed infine il quinto. Esiste una schedulazione che soddisfa tutte le richieste: lo stato sicuro.
9.
lettore2(){
while(true){
down(mutex);
nrlettori++;
if(nrlettori==1)
printf(primo
lettore);
up(mutex);
leggi_archivio();
down(mutex);
nrlettori--;
if(nrlettori==0)
printf(ultimo
lettore);
up(mutex);
}
}
printf(ultimo
lettore);
mutex=0;
}
}
lettore2(){
while(true){
while(mutex==1);
nrlettori++;
if(nrlettori==1)
printf(primo
lettore);
mutex=1;
leggi_archivio();
while(mutex=1);
nrlettori--;
if(nrlettori==0)
10.
printf(ultimo
lettore);
mutex=1;
}
}
PC2(){
while(true){
el=rimuovi();
consuma(el);
dato=produci();
inserisci(dato);
}
while(true){
val=rimuovi();
consuma(val);
elto=produci();
inserisci(elto);
}
}
Si tratta di due Thread concorrenti che si passano i dati attraverso una variabile
condivisa.
La procedura rimuovi() preleva il valore della variabile condivisa;
la procedura inserisci(x) scrive il valore x nella variabile condivisa.
I due thread funzionano sia da produttori che da consumatori: il primo preleva il dato
della variabile, lo consuma e scrive un altro dato nella variabile. Il secondo prende il
dato appena scritto, lo consuma e scrive un altro dato nella variabile condivisa.
In sostanza i due thread si passano lun laltro un dato alla volta.
Sincronizzare i due thread usando due semafori (gestiti con le procedure down() e up()),
tenendo conto che la variabile inizialmente contiene un elemento valido e che si desidera
far partire inizialmente il Thread PC1.
Dare i valori iniziali dei semafori.
La sincronizzazione viene effettuata con due semafori (sem1 e sem2, inizializzati cosi:
sem1=1, sem2=0) nel seguente modo:
PC1(){
while(true){
down(sem1);
el=rimuovi();
consuma(el);
dato=produci();
inserisci(dato);
up(sem2);
}
}
PC2(){
while(true){
down(sem2);
val=rimuovi();
consuma(val);
elto=produci();
inserisci(elto);
up(sem1);
}
}
11.
Si scriva il codice di una applicazione multithread in Java che utilizzi strutture semaforiche
realizzate anchesse in Java.
import java.io.*;
public class Semaf {
private int s;
Semaf(){ s=1;}
synchronized void down() {
if( s <= 0 ) { try{ wait();} catch(InterruptedException e){}; }
s--;
}
synchronized void up() {
s++;
notify();
}
}
System.out.print("l'esecuzione ");
System.out.println("P3 ");
a.up();
}
}
}
}
}
io sono il thread P1
invece io rappresento l'esecuzione P2
io sono il thread P3
io sono il thread P1
io sono il invece io rappresento thread l'esecuzione P3
P2
io sono il thread P3
...
12. Siano dati i seguenti tre task, rappresentati con dei blocchi di istruzioni in C:
T1:
{if(a==0) y=b; else z=0;}
T2:
{if(a==1) x=a+b; else c=0;}
T3:
{if(a==2) b=x+y; else x=1;}
Si consideri il sistema di tre task: C={ {T1,T2,T3}, 0}, cioe linsieme dei tre task senza
vincoli di precedenza.
Il sistema C, se eseguito in concorrenza, non e determinato.
Per quale coppia ( cioe{T1,T2}, {T1,T3}, {T2,T3}) di task lesecuzione concorrente e
determinata ? dimostrarlo mediante le condizioni di Bernstein.
Le condizioni di Bernstein dicono che se e solo se un insieme di task e mutuamente non
interferente, allora il sistema e determinato.
La condizione di non interferenza tra due task T1 e T2 e la seguente: se D(T) e R(T)
sono il dominio e il condominio del task T, devono essere verificate le tre condizioni:
R(T1) and R(T2) = 0
R(T1) and D(T2) = 0
R(T2) and D(T1) = 0
Nel nostro caso
D(T1) = {a,b}
D(T2) = {a,b}
R(T1) = {y,z}
R(T2) = {x,c}
D(T3) = {a,x,y}
R(T3) = {b,x}
Da cui si vede che tutti e tre i task hanno una intersezione non nulla degli R().
Prendendo le coppie, si vede anche che R(T1)andR(T2)=0, D(T1)andR(T2)=0,
D(T2)andR(T1)=0 quindi sono non interferenti. Le coppie T1,T3 e T2,T3 hanno
intersezioni non nulle.
Quindi lunica coppia di Task che genera una concorrenza determinata e T1,T2.
13. Si consideri un Sistema Operativo dotato di tre risorse, ciascuna con sette istanze. Il vettore
Capacita e dunque:
W=|7, 7, 7|
In un certo istante, la situazione del sistema e la seguente:
le risorse allocate sono descritte dalla matrice P e quelle richieste dalla matrice Q.
P ==
2
2
3
0
3
2
1
0
0
2
1
4
Q ==
0
1
2
5
0
0
0
0
0
1
3
0
180K
556K
160K
Quindi:
alloco 410 K
restano 180K
restano 146K
restano 160K
180K vengono allocati nella partizione di 256 K che parte da 256k. Memoria rimasta=76K
410K vengono allocati nella partizione di 512 K che parte da 1M. Memoria rimasta=102K
Quindi: