UniTO/anno2/Sem1/Galla/MCAD/lesson7-18102017.md
Francesco Mecca 271c386706 eginet
2019-04-29 17:55:57 +02:00

5.1 KiB

Semafori concorrenti - HHO

(scrivi il resto)

No deadlock

Per assurdo, supponiamo deadlock al tempo T: i semafori si bloccano su P (idrogeno su P(sH), ossigeno su uno dei P(sO))

  • Invarianti semaforici
val(sH,T) = 0
=> NP(sO, T) = NV(s0,T) // nessuna P eseguita con successo, il valore di sH e' 0 perche' il processo e' bloccato

val(sO,T) = 0
=> NP(sH, T) = NV(sH, T) + 2 
  • utilizziamo gli invarianti topologici
NP (sH, T) = NV (sO, T)
=> 0 <= NP(sO,T) - NV (sH,T) <= 1
  • Cerchiamo un assurdo:
NV (sH, T) + 2 = NP (sH, T) = NV (sO, T) <= NV (sH,T) + 1
  • Il risultato non e' ammissibile (2<1) quindi non e' possibile ottenere deadlock dalla soluzione presentata.

Uso di Semafori Privati

Semafori (binari/contatori) di proprieta' di uno o piu' processi. I proprietari sono gli unici a poter eseguire P sul suddetto semaforo, ma non ci sono restrizioni sull'uso di V.

Utilizzi

  • Svegliare un determinato processo, per rendere la gestione di una coda di processi indipendente dall'implementazione del semaforo (kernel space). In questo modo posso stabilire una gerarchia di prorita' tra processi.

Vantaggi

Precisione nella gestione di pool di processi.

Svantaggi

Complessita' aggiunta all'implementazione: il programma deve mantenere piu' semafori (anche a livello di kernel: i semafori privati possono essere implementati al di fuori di una coda). Il caso piu' comune e' la necessita' di dover mantenere un semaforo per ogni processo in coda, invece che uno solo (e una coda).

Schemi di gestione

Schema 1

Contesto:

< struttura dati gestore >
semaphore mutex;
semaphore priv[n] = {0}; // array di semafori privati, ognuno dedicato a un processo (n processi)

Procedura di acquisizione

public int acquisisci (int p) {
	// simile ai semafori condizionali, anche qui dovremo testare l'ottenibilita' di una risorsa
	int j; // risorsa da allocare
	P (mutex);
	if ( ! Cp) { // Cp e' la condizione per il processo p
		< registrare sospensione del processo p >
		V (mutex);
		P (priv[p]); 
		< registro che il processo p non e' piu' sospeso >
	}
	< alloco risorsa j >
	V (mutex);
	return j;
}

Procedura di rilascio

public void rilascio (int j) {
	int p;
	P (mutex);
	< registro rilascio risorsa j >
	if ( < esiste almeno 1 processo sospeso > ) {
		< scelgo il processo p con massima priorita' >
		V (priv[p]);
	} else {
		V (mutex);
	}
}

Esempio: allocazione di pool di risorse

int avail = M;
bool libero[M] = {true};
bool sospeso[n] = {false};
int cnts = 0; // conta i sospesi
semaphore mutex;
semaphore priv[n] = {0}; // array di semafori privati, ognuno dedicato a un processo (n processi)

Procedura di acquisizione

public int acquisisci (int p) {
	int j; // risorsa da allocare
	P (mutex);
	if ( avail == 0) { 
		sospeso[p] = true;
		cnts++;
		V (mutex);
		P (priv[p]);
		sospeso[p] = false;
		cnts--;
	}
	avail--;
	while (libero[j]) j++;
	libero[i] = false;
	V (mutex);
	return j;
}

Procedura di rilascio

public void rilascio (int j) {
	int p;
	P (mutex);
	avail++;
	libero[j] = true;
	if ( cnts > 0 ) {
		p = maxpriority (sospeso); // ottengo il processo con priorita' massima
		V (priv[p]);
	} else {
		V (mutex);
	}
}

Schema 2

Siccome e' complesso dal punto di vista della programmazione strutturata (alto livello), e' stato proposto un secondo schema.

Procedura di acquisizione

public void acquisizione (int p) {
	P (mutex);
	if (Cp) {
		< alloca risorsa >
		V (priv[p]);
	} else {
		< registro sospensione processo p>
	}
	V (mutex);
	P (priv[p]); // a seconda del valore di Cp, questa P e' sospensiva (!Cp) o vanificata dalla V (Cp)
}

nota: la risorsa in questo caso e' garantita dal processo che rilascia, perche' se Cp non e' verificata, questa procedura non alloca nulla

Procedura di rilascio

public void rilascio (int j) {
	int p;
	P (mutex);
	< registro rilascio risorsa j >
	if ( < esiste almeno 1 processo sospeso > ) {
		< scelgo il processo p con massima priorita' >
		< alloco risorsa >
		< registro che p non e' sospeso >
		V (priv[p]);
	}
	V (mutex);
}

Pro e contro della soluzione 2

  • Un problema della soluzione 2 e' che e' limitato a 1 dato libero per volta (se i processi consumatori hanno bisogno di piu' di un "dato" (risorsa), non ho modo di sapere quanti ne sto rilasciando), mentre la soluzione 1 implementata con un while risolve questo problema.

  • Siccome il processo rilasciante si occupa anche di allocare le risorse, non si puo' effettuare il passaggio di parametri. Questo caso e' ovviato dall'utilizzo di un array condiviso allocazione che fornisce l'indice della risorsa che e' stata allocata.

Problema dell'accesso unico (senso unico alternato) - Readers & Writers

Precedenza agli scrittori

L'implementazione con precedenza agli scrittori permette di bloccare i readers se un writer e' nella SC, ma anche se un writer e' in attesa.

Soluzione bilanciata

Utilizzando dei semafori privati, e' possibile associare un semaforo privato a lettori e scrittori, ottenendo un accesso limitato degli scrittori alla SC, e quindi ottenendo una soluzione equilibrata.