Prima provetta di Sistemi Operativi 04/11/2011 SOLUZIONI 1. (1pt

annuncio pubblicitario
Prima provetta di Sistemi Operativi 04/11/2011
SOLUZIONI
1. (1pt) Elencare e commentare brevemente i vantaggi/svantaggi dei Thread.
I Threads sono una implementazione della concorrenza con condivisione dell’ambiente.
Vantaggi: velocitá del context switch, maggiore efficienza dovuta al fatto che un thread
bloccante non blocca gli altri, possibilitá di schedulare i thread su piú core, se disponibili, arrivando ad una programmazione parallela. Svantaggi: la condivisione delle
risorse tra thread deve essere gestita e puo’ richedere un certo sovraccarico.
2. (3pt) Alla biblioteca tecnico-scientifica dell’Universita’ di Trieste c’e’ un terminale per le consultazioni. Entrando, vi accorgete che c’e’ una persona che sta consultando il terminale e che due persone stanno aspettando in coda. Calcolare la probabilita
di dover aspettare pi di 30 minuti (Prob[tempo attesa¿30minuti]) supponendo che il
tempo d’attesa sia una variable aleatoria esponenziale con media di 10minuti.
Il significato del tempo d’attesa non é specificato. Si propongono due possibili
interpretazioni.
La prima é semplicemente di considerare il tempo d’attesa complessivo una variabile casuale distribuita esponenzialmente cioé con distribuzione f (t) = λe−λt con
media R1/λ=10 (per cui λ = 0.1) della quale si vuole determinare la prob. che P (t >
∞
−3
30) = 30 λe−λt dt = −e−λt |∞
= 0.05.
30 = e
Nella seconda interpretazione ogni utente presenta un tempo d’attesa distribuito
esponenzialmente con media di 10minuti. Questo vuol dire che l’evento delle fine consultazioni forma un processo di Poisson con media 10minuti.La probabilitá di avere
n
−λt
. Visto che ci sono tre utenti ciascuno dei
n fine consultazioni in t é data da λt)
n! e
quali puó terminare la consultazione, la probabiliá che il nuovo utente aspetti piú di
30 minuti é la probabiliá di non avere né 0 né 1 né 2 fine consultazioni in 30 minuti
1
2
0
−λ30
−λ30
−λ30
cioé (1 − λ30)
) ∗ (1 − λ30)
) ∗ (1 − λ30)
) = 0.63
0! e
1! e
2! e
3. (4pt) Un WebServer puó essere modellato come segue
Gli utenti accedono al server con frequenza pari a 50 richieste al secondo, il server
recupera le informazioni richieste e le presenta agli utenti. Le informazioni alle quali
1
il server accede sono contenute in due dischi, ciascuno con frequenza di servizio µ2 e
µ3 pari a 50. Le probabilitá alle quali vengono richieste le informazioni sui due dischi
sono rispettivamente pari a 0.2 e 0.3.
Calcolare la frequenza di servizio della CPU tale il numero medio di utenti presenti
nel server sia pari a 50.
Chiamando λ1, λ2 e λ3 le frequenze di arrivo sulla CPU, sul primo e sul secondo
disco rispettivamente, si ha λ1 = λ + λ2 + λ3, e λ2 = 0.2λ1, λ3 = 0.3λ1 per cui
λ1 = 2λ = 100, λ2 = 20, λ3 = 30. Quindi ρ1 = 100/µ1, ρ2 = 2/5, ρ3 = 3/5. Il
ρ2
ρ3
ρ1
+ 1−ρ2
+ 1−ρ3
. Se impongo
numero complessivo degli utenti nel server é N = 1−ρ1
N=50, e semplificando un pó, si ha 50 = 100/(µ1 − 100) + 2/3 + 3/2 da cui µ1 =
100 + 50/47.83 = 102.09
4. (2pt) Calcolare la frequenza media di arrivo l al Webserver del punto 3 tale che
il tempo medio di attesa nella coda CPU sia minore di 10 secondi.
ricorda: il numero medio di processi in coda ρ2 /(1 − ρ)
Innanzitutto usiamo il valore di µ1 calcolato al punto 3 per semplificare le cose,
2
ρ12
= µ12λ1
cioé µ1 = 102.09. il numero medio di utenti in coda é 1−ρ1
−λ1µ1 . Per Little,
il numero medio N di utenti in coda CPU é il prodotto della frequenza di arrivo in
λ12
coda per il tempo d’attesa in coda: N = λ1T per cui λ1 = N/T = (µ12 −λ1µ1)T
.
2
T µ1
Risolvendo questa equazione rispetto a λ1 si ha: λ1 = (1+T µ1)=101.99
Visto che λ1 =
2λ si ha λ = λ1/2 = 50.99 arrivi al secondo al Server.
5. (4pt) Tracciare il diagramma delle precedenze di un programma concorrente
multithread per eseguire il seguenze prodotto scalare: (x*A)*(B*y) dove A e B sono
due matrici con dimensione 2x3 e 3x2 rispettivamente, x, y sono due vettori di dimensione 1x2 e 2x1 rispettivamente, e il simbolo * rappresenta il prodotto righe per
colonne. Implementare il diagramma usando la programmazione multithread in Java.
I passi fondamentali di una possibile semplice implementazione sono illustrati nel seguito (nella classe data ci sono ovviamente le matrici A,B,X,Y,row,col e il valore result).
public class XbyA extends Thread{ //prodotto del vettore riga X con la colonna ind di A
data d; int ind; float s; int i;
public XbyA(data d, int indxi){this.d=d; this.ind=indxi;}
public void run(){
s=0;
for(i=0;i<2;i++)
s+=data.X[i]*data.A[i][ind];
data.row[ind]=s;
}
public class BbyY extends Thread{ //prodotto della riga ind di B con il vettore colonna Y
data d; int ind; float s; int i;
public BbyY(data d, int indxi){this.d=d; this.ind=indxi;}
public void run(){
s=0;
for(i=0;i<2;i++)
s+=data.B[ind][i]*data.Y[i];
data.col[ind]=s;
}
public class RbyC extends Thread{
2
data d; int i; float s;
public RbyC(data d)[this.d=d;}
public void run(){
s=0;
for(i=0;i<3;i++)
s+=data.row[i]*data.col[i];
data.result=s;
}
}
public class principale {
private static data d= new data();
public principale() {}
public static void main(String[] args) {
XbyA t1[]=new XbyA[3];
BbyY t2[]=new BbyY[3];
for(int i=0; i<3; i++){//istanzia i prodotti e fa partire
t1[i]= new XbyA(d,i);
t1[i].start();
}
for(int i=0; i<3; i++){//istanzia i prodotti e fa partire
t2[i]= new BbyY(d,i);
t2[i].start();
}
for(int i=0; i<3; i++){
t1[i].join();
}
for(int i=0; i<3; i++){
t2[i].join();
}
z=new RbyC(d);//quando sono finiti i prodotti in concorrenza faccio row x col
z.start();
}
Il diagramma delle precedenze sará allora:
6. (3pt) Scrivere lo pseudocodice di due metodi in Java per realizzare le primitive
cobegin(lista dei thread) e coend(lista dei thread) mediante start e join.
Uno schema puó essere:
public class co{
T t[]=new T[N];
for(i=0;i<N;i++)
t[i]= new T(i);
3
cobegin(t,N){
for(int i=0; i<N; i++){t[i].start();}
}
coend(t,N){
for(int i=0; i<N; i++){t[i].join();}
}
}
dove T é definito come Thread e N é il loro numero.
7. (3pt) Scrivere lo pseudocodice del processo che gestisce il generico accesso
al Webserver del punto 3 con il vincolo che non ci possano essere piú di 50 utenti
che accedono al server. Realizzare il vincolo mediante un semaforo. Il programma
controlla anche l’accesso del Webmaster bloccandolo se c’é almeno un accesso attivo.
Si puó usare il problema dei lettori scrittori con un semaforo per bloccare gli utenti
se maggiori di 50.Il processo puó essere realizzato come segue:
Server()
{
down(web);
down(mutex);
n++; if(n==1) down(wm);
up(mutex);
recupera(); visualizza();
down(mutex);
n--; if(n==0) up(wm);
up(mutex);
up(web);
}
webmaster()
{
down(wm);
aggiorna();
up(wm);
}
Il meccanismo funziona cosı́: Server é un thread che viene attivato ad ogni accesso
di un utente. ’web’ é un semaforo inizailizzato a 50. wm, mutex sono semafori binari
inizalizzati a 1. Se ci sono 50 utenti, ci sono 50 thread attivi e ’web’ é a 0. Il 51-esimo
utente si blocca su down(web) e aspetta che un utente finisca per alzare web. Quando
tutti gli utenti finiscono web torna a 50.
8. (3pt) Che differenza c’ tra un semaforo spinlock e un semaforo waitlock?
4
Quando conveniente usare l’uno o l’altro? Fare un esempio di implementazione di
un semaforo spinlock.
Un semaforo spinlock aspetta usando attesa attiva mentre un semaforo waitlock
cambia coda d’attesa e non consuma cicli macchina. Peró i semafori waitlock sono
piú complessi degli spinlock dovendo essere gestiti dal kernel. É conveniente usare
spinlock per sezioni critiche brevi e waitlock per sezioni critiche piú lunghe. Un esempio puó essere la primitiva atomica TSL(b) che restituisce il valore logico di b e lo
mette a true. Esempio di utilizzo:
lock()
{
while(TSL(b)) ;
}
unlock()
{
b=0;
}
9. (4pt) Implementare il seguente grafo usando processi concorrenti realizzati con
le chiamate di sistema fork/waitpid. Se fossero thread come si potrebbe implementare
il grafo con le primitive cobegin/coend?
Innazitutto il grafo viene semplificato portandono alla seguente forma:
5
Questo semplice grafo puó essere implementato con processi concorrenti segcondo il
seguente flusso (naturalmente non é l’unico!)
Implementando questo flusso mediante processi concorrenti usando fork/waitpid abbiamo:
if((t0=fork())==0){exec(T0)}
else{
waitpid(t0);
if((t1=fork())==0){exec(T1)}
else{
if((t3=fork())==0){exec(T3)}
else{
if((t4=fork())==0){exec(T4)}
else{
if((t2=fork())==0){exec(T2)}
else{
waitpid(t3); waitpid(t1);
waitpid(t4); waitpid(t2);
if((t5=fork())==0){exec(T5)}
else{
if((t6=fork())==0){exec(T6)}
else{
waitpid(t5); waitpid(t6);
if((t7=fork())==0){exec(T7)}
}
}
}
}
}
}
6
}
Implementando il grafo con le primitive cobegin/coend abbiamo:
T0();
cobegin
begin
cobegin
T1(); T3();
coend;
T5();
end;
begin
cobegin
T2(); T4();
coend;
T6();
end;
coend;
T7();
10. (2pt) Si consideri il seguente pseudocodice relativo al problema dei 5 filosofi
con un semaforo per ogni forchetta.
class Filosofo extends Thread{
int fid; Semaforo sx, dx;
public Filosofo(int i){this.fid=i}
public void run(){
while(true) {
Pensa(i); Affamato(i);
sx.down(); dx.down();
Mangia(i);
sx.up(); dx.up();
}
}
}
Discutere questa soluzione e proporre una variante nel caso questa soluzione porti ad
uno stallo.
In questa soluzione, tutti i filosofi prendono la forchetta di sinistra e di destra rispettivamente, bloccandola. Questa soluzione porta sicuramente ad uno stallo perché se
tutti i filosofi concorrentemente prendono le loro forchette di sinistra poi tutti cercano
le forchette di destra ma vedono che sono giá impegnate e si bloccano. Una possibile soluzione é di mettere la down di un semaforo inizializzato a 4 prima di prendere le forchette e mettere una up dello stesso dopo averle rilasciate. A questo punto
7
posso avere al piú 4 filosofi concorrenti; il quinto si blocca e non blocca la forchetta.
Un’altra possibilitá é di serializzare i filosofi mettendo un down(mutex) dopo il while
e un up(mutex) dopo aver rilasciato le forchette. In questo modo non ho stallo ma non
ho piú filosofi concorrenti.
11. (3pt) In un certo istante la situazione riguardo le risorse usate da 4 processi
concorrenti la seguente:
Risorse Allocate
R1 R2 R3
Proc.1
2 3 1
Proc.2
4 4 0
Proc.3
2 2 0
Proc.4
4 2 3
Massimo numero di risorse richieste dai processi
R4
R1 R2 R3 R4
0
4 4 3 2
0
5 5 5 5
0
4 4 3 2
2
4 4 3 2
mentre le 4 risorse sono disponibili nel seguente numero di istanze: W=[16, 15, 8, 6].
Verificare se questo uno stato sicuro o meno applicando l’Algoritmo del Banchiere.
Il problema si puó risolvere costruendo la matrice delle richieste, Q=[matrice del
numero massimo di risorse richieste] - [matrice delle risorse allocate]. Nel nostro caso
Risorse Allocate
R1 R2 R3
Proc.1
2 3 1
Proc.2
4 4 0
Proc.3
2 2 0
Proc.4
4 2 3
R4
0
0
0
2
Risorse Richieste
R1 R2 R3 R4
2 1 2 2
1 1 5 5
2 2 3 2
0 2 0 0
risorse disponibili: [4 4 4 4]
Quindi, puó essere soddisfatta la richiesta di Proc1 che rilasciando le risorse aumenta
le risorse disponibili a [6 7 5 4] poi Proc3 portando le risorse disponibili a [8 9 5
4] poi Proc4 che porta le risorse disponibili a [12 11 8 6] e infine Proc2 che quando
rilascia le risorse le riporta a [16 15 8 6]. Quindi lo stato é sicuro.
8
Scarica