Sunteți pe pagina 1din 16

SIMULARE DE SISTEME

Specificarea problemelor.

O problemă se poate specifica la nivelul interfeţei dintre algoritmul ce o rezolvă şi mediul extern.
O specificare de problemă P este o mulţime de evenimente de intrare in(P), o mulţime de evenimente de ieşire out(P) (cele două mulţimi constituind interfaţa lui P) şi o
mulţime seq(P) de secvenţe admisibile de evenimente de intrare şi evenimente de ieşire (care precizează interleavingurile permise dintre intrări şi ieşiri).
Fiecare eveniment de intrare sau de ieşire are un nume şi poate avea asociate date ca parametri.

Exemplu. Problema excluderii mutuale pentru n procesoare:

 evenimente de intrare: Ti, Ei, 0  i  n-1, unde


 Ti  indică faptul că utilizatorul i doreşte să încerce să intre în secţiunea critică.
 Ei  indică faptul că utilizatorul i doreşte să iasă din secţiunea critică.
 evenimentele de ieşire: Ci, Ri 0  i  n-1, unde
 Ci  indică faptul că utilizatorul i poate acum să intre în secţiunea critică.
 Ri  indică faptul că utilizatorul i poate acum să intre în secţiunea remainder.
 o secvenţă  a acestor evenimente este admisibilă dacă pentru i:
1. |i ciclează (Ti,Ci,Ei,Ri)
2. ori de câte ori Ci apare în , atunci j eveniment asociat lui j ce apare în  înaintea lui Ci nu este lj.

(Condiţia 1 arată că utilizatorul şi algoritmul interacţionază propriu; ea impune utilizatorului un mod corect de comportare. Condiţia 2 afirmă că nu  doi utilizatori simultan în
secţiunea critică).
Sisteme de comunicaţie
În problemele de simulare vom fi interesaţi în implementarea unor anumite tipuri de sisteme de comunicaţie între procesoare pe baza unor primitive.
Aceste sisteme de comunicaţie vor fi descrise cu ajutorul unei specificaţii de problemă.

Exemple.

1. Transmiterea asincronă de measej point-to-point

interfaţa:
 sendi (M): eveniment de intrare asociat procesorului pi care trimite o mulţime M (eventual vidă) de mesaje. Fiecare mesaj include o indicaţie a trimiţătorului şi a
destinatarului; există cel mult un mesaj pentru fiecare destinatar şi perechea transmiţător-destinatar trebuie să fie compatibilă cu topologia sistemului.
 recvi(M): eveniment de ieşire asociat procesorului pi în care o mulţime M de mesaje este primită. Fiecare mesaj trebuie să-l aibă pe pi ca destinatar.
mulţimea secvenţelor admisibile.
Fie  o secvenţă de evenimente de intrare şi ieşire.
Notăm cu R mulţimea tuturor mesajelor ce apar în toate evenimentele recv i(M) din  şi cu S mulţimea tuturor mesajelor ce apar în toate evenimentele send i(M) din .
Există o funcţie K:RS care asociază fiecărui mesaj din R un mesaj cu acelaşi conţinut din S a.î.:
 Integrity: K este bine definită. Orice mesaj primit a fost în prealabil trimis. Nu este permisă corupţia mesajelor.

 No duplicates: K este injectivă. Nici un mesaj nu este primit mai mult de o dată.

 Liveness: K este surjectivă. Orice mesaj trimis va fi primit.

Observaţie. Aceste proprietăţi vor fi modificatecând se vor considera şi defecţiunile în sistem.

Broadcast asincron

interfaţa (la un sistem cu broadcast asincron de bază).


 bc-sendi(m): eveniment de intrare în care procesorul pi, trimite un mesaj m
tuturor procesoarelor.
 bc-recvi(m,j): eveniment de ieşire în care procesorul p primeşte mesajul m, în prealabil difuzat de p . i j

mulţimea secvenţelor admisibile. Fie  o secvenţă de evenimente de intrare şi de ieşire.

bc-recvi(m,j) din  un evniment precedent bc-sendj(m) a.î.


Există K o aplicaţie care asociază fiecărui

 Integrity: K este bine definită (orice mesaj primit a fost în prealabil trimis).
 No duplicates: Pentru ficare procesor pi 0  i  n-1, restricţia lui K la evenimentele bc-recvi este
injectivă (nici un mesaj nu este primit mai mult de o dată de un procesor).
 Liveness: Pentru ficare procesor pi 0  i  n-1, restricţia lui K la evenimentele bc-recvi este surjectivă
(orice mesaj trimis este primit de fiecare procesor).
Observaţie: Din nou s-a presupus că nu există defecţiuni.

Procese
Pe fiecare procesor se vor executa bucăţi de cod care implementează sistemul de comunicaţie dorit. Deci nu va mai fi corect să identificăm "algoritmul" cu procesorul,
pentru că avem procese multiple ce se execută pe acelaşi procesor. Vom folosi noţiunea de nod iar sistemul de comunicaţie va fi reprezentat ca o cutie neagră în care este implementată
o "stivă de protocoale".

mediul extern

Nivelul 1

Nivelul 2

Nivelul 3

sistemul de comunicaţie

Un sistem este o colecţie de noduri p0,...,pn-1, un sistem de comunicaţie C şi un mediu extern E.


C şi E nu sunt explicit modelate ca procese. Ele vor fi date ca specificaţii de probleme, care impun condiţii asupra comportării lor.
În fiecare nod există o stivă de nivele cu acelaşi număr de nivele. Fiecare nivel interior comunică cu cele două nivele adiacente. Nivelul de jos comunică cu sistemul de
comunicaţie şi nivelul de sus cu mediul extern.
Fiecare proces este modelat ca un automat cu o mulţime (posibil infinită) de stări şi o mulţime de stări iniţiale. Tranziţiile dintre stări sunt declanşate de apariţia
evenimentelor procesului. Un proces are patru tipuri de evenimente:
1. intrări din nivelul de deasupra (sau nivelul exterior dacă este nivelul de sus).
2. ieşiri către nivelul de deasupra.
3. intrări din nivelul de dedesubt (sau sistemul de comunicaţii dacă este nivelul de jos) .
4. ieşiri către nivelul de dedesubt.
Evenimentele de tipul 1 şi 2: interfaţa – top a procesului
Evenimentele de tipul 3 şi 4: interfaţa – bottom a procesului.

Un eveniment este activat într-o stare a procesului dacă există o tranziţie din acea stare, etichetată cu acel eveniment.
Un eveniment de intrare poate fi activat în orice stare a procesului.
Evenimentele de ieşire sunt activate de funcţia de tranziţie a procesului.
Intrările din mediul extern şi din sistemul de comunicaţii sunt intrările nodului.
Procesele de pe un singur nod interacţionează a.î. numai un număr finit de evenimente (diferit de intrările nodului) sunt activate de o intrare în nod.

O configuraţie a sistemului specifică o stare pentru orice proces de pe orice nod (configuraţia nu include starea sistemului de comunicaţie). O configuraţie iniţială conţine numai stări
iniţiale ale proceselor.

O execuţie a sistemului este un şir C0, 1, C1, 2,C2,.... alternativ de configuraţii şi evenimente, începând cu o configuraţie şi, dacă este finit, sfârşind cu o configuraţie a.î.:

1. Configuraţia C0 este o configuraţie iniţială.


2. i  1 evenimentul i este activat în configuraţia Ci-1 şi configuraţia Ci este rezultatul lui i acţionând pe Ci-1. (Orice componentă de stare este aceeaşi în C i ca şi în Ci-
1 cu excepţia (cel mult două) proceselor pantru care i este eveniment). Starile acestor procese se schimbă conform funcţiei de tranziţie a acestor procese.
3. i  1, dacă i nu este o intrare pentru un nod, atunci i > 1 şi este un eveniment pe acelaşi nod ca şi i-1. (Primul eveniment trebuie să fie o intrare pe un nod).
4. i  1 dacă i estte intrare pe un nod atunci în Ci-1 nu a fost avtivat nici un alt eveniment (diferit de un eveniment de intrare în nod).

Observaţie: Din 3. şi 4. rezultă că un nod este declanşat de o intrare în nod (din exterior sau din sistemul de comunicaţie) şi această declanşare cauzează un lanţ de evenimente pe acest
nod care apare atomic până ce alet evenimente nu mai sunt declanşate.
Planificarea unei execuţii este şirul evenimentelor din execuţie.
Dacă  este o execuţie atunci top() (respectivc bot()) este restricţia planificării lui  la evenimentele din inerfaţa top (respectiv bottom) a nivelului top (respectiv bottom).

O execuţie este fair dacă orice eveniment (diferit de o intrare într-un nod) care este activat va apare (odată şi odată; execuţia nu se opreşte prematur, când mai există paşi de urmat).
O execuţie  are calitatea de a fi user-compliant pentru o specificaţie de problemă P, dacă mediul extern satisface restricţiile de intrare ale lui P: pentru orice prefix ' a lui  unde 
este o intrare din mediul extern, dacă ' este prefix al unui element din seq(P), atunci aşa este şi '.
O execuţie este corectă pentru sistemul de comunicaţie C dacă bot() este un element al lui seq(C). (Sistemul de comunicaţie este corect, în conformitate cu specificaţiile de problemă
a lui C).
O execuţie este (P,C) – admisibilă dacă estge fair, user – compliant pentru specificaţia de problemă P şi corectă pentru sistemul de comunicaţii C.
Atunci când P şi C sunt clare din context execuţia se va numi admisibilă.

Simulare
Sistemul de comunicaţii C, simulează (global) sistemul de comunicaţii C2 dacă există o colecţie de procese unul pentru fiecare nod numit Sim (procesul de simulare) care
satisface:
1. Interfaţa top a lui Sim este interfaţa lui C2.
2. Interfaţa bottom alui Sim este interfaţa lui C1.
3. Pentru orice execuţie (C2, C1) – admisibilă a lui Sim există un şir  în seq(C2) a.î. =top().

(Executia simulării în topul sistemului de comunicaţie C1 produce aceleaşi apariţii în mediul extern pe care le face sistemul de comunicaţii C2).

C2 input C2 output C2 input C2 output

C2
Sim
C1 input C1 output

C1

O definiţie mai slabă a simulării, este simularea locală.


O execuţie  este local user – compliant pentru o specificaţie de problemă P, dacă  ' un prefix al lui , unde  este o intrare din mediul extern, dacă  'seq(P) a.î. '|i este
prefix al lui '|i pentru i , atunci  seq(P) a.î. '|i este un prefix al lui '|i pentru i (mediul extern satisface restricţiile de intrare ale lui P pentru fiecare nod, dar nu neapărat
global).
O execuţie este (P,C) – local admisibilă dacă este fair, local user – compliant şi corectă pentru sistemul de comunicaţii C.
Definiţia simulării locale este aceeaşi cu a simulării globale cu excepţia condiţiei 3 care devine acum:

3'. Pentru orice execuţie (C2, C1) – local admisibilă  a lui Sim  seq(C2) a.î. |i=top()|i i 0  i  n-1.

Convenţii de pseudocod

Vom descrie un algoritm asincron de tip MP ca o listă de evenimente de intrare şi ieşire.


Fiecare eveniment va avea un nume şi se vor descrie schimbările locale care vor fi cauzate de apariţia acestui eveniment, într-un pseudocod secvenţia uzual.
Pe lângă schimbările locale de stare, apariţia unui eveniment poate cauza şi activarea unor evenimante de ieşire. Acestea vor fi indicate astfel: "enable input X". Dacă
rezultatul unui eveniment de ieşire este dezactivarea lui (nu se fac alte schimbări locale) nu va fi trecut în lista de evenimente.

Broadcast and multicast

Pentru a specifica calitatea unui serviciu de broadcast (proprietăţi ce vor fi descrise mai jos) interfaţa descrisă mai anterior pentru un serviciu broadcast de bază se modifică
astfel:
 bc-sendi(m,qos): eveniment de intrare asociat procesorului p i, care trimite un mesaj m tuturor procesoarelor; qos este un parametru ce descrie calitatea serviciului
cerut.
 bc-recvi(m,j,qos): eveniment de ieşire în care procesorul pi primeşte mesajul m în prealabil difuzat de pj; qos este ca mai sus.

Observaţie: Atunci când ne referim la un serviciu broadcast de bază vom considera qos=basic. Un mesaj difuzat este primit şi de transmiţător.
Operaţiile bc – send şi bc – recv nu sunt blocante: nu se aşteaptă ca un mesaj să fie primit de toate procesoarele iar operaţia bc – recv acţionează în modul de lucru bazat pe întreruperi.

Se pot discuta două categorii de calităţi prin adăugarea de noi proprietăţi pentru secvenţele admisibile (pe lângă cele ale situaţiei qos=basic: integrity, no duplicates, liveness).

Ordonare

Un sistem de broadcast single-source FIFO (qos=ssf) satisface proprietăţile lui basic şi

Single-Source FIFO:  m1, m2 şi  pi, pj dacă pi trimite m1 înainte de al


trimite pe m2 , atunci pj nu-l primeşte pe m2 înaintea lui m1.

Un sistem de broadcast total ordonat (qos=to) satisface proprietăţile lui basic şi

Totally Ordered:  m1, m2 şi  pi, pj dacă m1 este primit în pi înaintea lui m2 , atunci m2 nu este primit în pj înaintea lui m1.
În cele două condiţii, primit este un bc – recv cu qos pe una din cele două valori iar trimis este un bc – send cu qos corespunzător.

Pentru a defini al treilea model de broadcast cu ordonare este necesar să extindem relaţia de apariţie a unui eveniment înaintea altuia, la mesaje.
Se presupune că toate comunicările de la un nivel superior al aplicaţiei se execută numai cu bc – send şi bc – recv (nu există mesaje "behind of the scenes").

Definiţie: Dată o secvenţă de evenimente bc – send şi bc – recv spunem că mesajul m 1 se întâmplă înaintea mesajului m2 dacă
1. evenimentul bc – recv pentru m1 se întâmplă înaintea evenimentului bc – send pentru m2 sau
2. m1 şi m2 sunt trimise de acelaşi procesor şi m1 este trimis înaintea lui m2.

Un sistem de broadcast ordonat cauzal, qos=co, satisface proprietăţile lui basic şi

Causally Ordered:  m1, m2 şi  pi dacă m1 se întâmplă înaintea lui m2 atunci m2 nu-i primit de pi înaintea lui m1.

Exemple:

a a ,b

cauzal ordonat
dar nu-i t.o.

b b, a

a b b, a
t.o
dar nu-i cauzal ordonat

b, a

a b, a

ssf dar nu-i t.o.


şi nici cauzal ordonat

a, b
b

Siguranţa în funcţionare (Reliability)

Un sistem de broadcast este reliable (în prezenţa a f defecte) dacă pentru orice secvenţă  de bc – send şi bc – recv sunt satisfăcute condiţiile: Integrity, No duplicates iar Liveness se
modifică astfel:

Nonfaulty Liveness: Dacă se consideră restrictia lui K la evenimentele asociate procesoarelor nedefecte atunci K este surjectivă (toate mesajele difuzate de un procesor nedefect sunt
primite odată şi odată de procesoarele nedefecte).
Faulty Liveness: Dacă un procesor nedefect are un eveniment bc – recv căruia K îi asociază un eveniment bc – send al unui procesor defect, atunci orice procesor nedefect are un
eveniment bc – recv căruia K îi asociază acelaşi eveniment bc – send (orice mesaj trimis de un procesor defect este sau primit de toate procesoarele nedefecte sau de niciunul).

Observaţie: Condiţiile nu depind de tipul de defecţiune a procesoarelor; vom considera totuşi numai defecţiuni de tip crash.

Anumite combinaţii de proprietăţilor ale serviciilor de tip broadcast au nume specifice date de importanţa lor practică:

broadcast atomic = broadcast reliable cu t.o.


broadcast FIFO atomic = broadcast atomic cu ssf.
broadcast cauzal atomic = broadcast atomic cu c.o.
(ultimele două sunt echivalente).

Implementarea serviciilor de broadcast


1. Basic

Se implementează simplu în topul unui sistem point-to-point MP fără defecţiuni.


Când apare un bc – send pentru un mesaj m la un procesor pi, pi foloseşte sistemul point-to-point MP pentru a trimite o copie a lui m la toate procesoarele.
Când un procesor primeşte mesajul de la pi pe sistemul point-to-point, execută evenimentul bc – recv pentru m şi i.
2. ssf

Se implementează în topul unui sistem basic. Fiecare procesor asignează un număr de ordine pentru fiecare mesaj pe care îl difuzează; acesta este incrementat cu 1 ori de
câte ori un nou mesaj este difuzat. Primitorul unui mesaj de la p i cu număr de secvenţă T aşteaptă până se execută bc - recv i cu qos=ssf a tuturor mesajelor de la pi cu numărul de ordine
mai mic decât T.

3. t.o.

Un algoritm nesimetric

Se bazează din nou pe un sistem de broadcast basic. Pentru a difuza un mesaj m, procesorul p i îl trimite utilizând basic unui procesor central p c care asignează un număr de
ordine şi îl difuzează folosind basic.
Procesoarele execută receive pentru sitemul cu t.o. în ordinea specificată de numărul de ordine al mesajelor, aşteptând dacă este necesar până primesc toate mesajele cu
număr de ordine mai mic.
Clar, ordinea în care se execută receive pentru sistemul cu t.o. este aceeaşi la toate procesoarele şi anume cea în care au fost trimise de procesorul central.
Rolul procesorului central poate fi preluat şi de alte procesoare cu ajutorul unui token de identificare care să circule printre procesoare.

Un algoritm nesimetric

Ideea este de a asigna timestamp-uri mesajelor.


Algoritmul se bazează pe un sistem de broadcast cu calităţi ssf.

Agoritm de broadcast t.o. (codul pentru pi, 0  i  n-1)

Iniţial ts[j]=0, 0  j  n-1 şi pending este vidă.

1: atunci când apare bc – sendi(m,to):


2: ts[i]:=ts[i] +1
3: adaugă<m,ts[i],i> la pending
4: enable bc – sendi(<m,ts[i]>,ssf)

5: atunci când apare bc – recvi(<m,T>,j,ssf) ji :


// ignoră mesajele către el însuşi
6: ts[j]:=T
7: adaugă<m,T,j> la pending
8: if T>ts[i] then
9: ts[i]:=T
10: enable bc – sendi(<ts-up,T>,ssf)
//difuzează un mesaj de actualizare a timestamp-ului

11: atunci când bc - recvi(<ts-up,T>,j,ssf) ji:


12: ts[j]:=T

13: enable bc – recvi(m,j,to) atunci când


14: <m,T,j> este intrarea în pending cu cel mai mic (T,j) şi
15: T  ts[k] k
16: result: remove<m,T,j> din pending

Explicaţii:
Fiecare procesor menţine un contor crescător: timestamp-ul său, folosit pentru a ştampila mesajele pe care vrea să le difuzeze. În plus, fiecare procesor are un vector în care
estimează timestamp-urile celorlalte procesoare. Intrarea j din acest vector semnifică faptul că p i nu va primi (încă o dată) un mesaj de la p j cu un timestamp mai mic sau egal cu acea
valoare.
Aceste componente ale vectorului sunt actualizate cu ajutorul timestampe-urilor asociate mesajelor sau folosind un mesaj special de actualizare trimis de p j.
Timestamp-ul propriu al fiecărui procesor va fi mai mare sau egal cu estimarea sa a timestamp-urilor celorlalte procesoare. Atunci când procesorul trbuie să-şi marească
timestamp-ul pentru a asigura condiţia precedentă va trimite un mesaj de actualizare a timestamp-urilor tuturor celorlalte procesoare.
Fiecare procesor menţine o mulţime de mesaje ce aşteaptă să fie primite (pentru sistemul cu t.o.). Un mesaj cu timestamp-ul T este primit t.o., atunci când orice intrare în
vector este mai mare sau egală decât T. Fie  o execuţie fair a algoritmului care este corectă pentru sistemul de comunicaţii ssf.
Lema 1: Pentru procesor pi şi bc – sendi(m,to) din , pi asignează lui m un timestamp unic şi aceste timestamp-uri sunt asignate în ordine crescătoare în raport cu apariţiile lor în .

Lema 2: Timestamp-urile asignate mesajelor din , împreună cu id-urile procesoarelor formează o ordine totală (această ordine este numită ordinea timestamp).

Teoremă: Algoritmul precedent este algoritm de broadcast t.o.

Demonstraţie:
Trebuie demonstrat că au loc condiţiile:
Integrity, No Duplicates, Liveness şi Total Ordering.

Primele două sunt satisfăcute deoarece sistemul ssf pe care se bazează algoritmul le satisface.

Liveness: Presupunem că există un procesor pi care are o intrare în pending care aşteptă la nesfârşit. Fie <m,T,j> intrarea blocată în pending cu cel mai mic (T,j).
Întrucât procesoarele asignează timestamp-urile în ordine crescătoare odată şi odată <m,T,j> va fi cea mai mică intrare în mulţimea pending a lui p i. Pentru a fi blocată
trbuie să existe k a.î. T>ts[k] în p i la nesfârşit. Deci la un moment dat p i nu mai creşte ts[k]. Fie T' cea mai mare valoare pe care p i o dă lui ts[k]. Clar, ki. Deci de la un loc încolo p i nu
mai primeşte mesaje de la p k (în sistemul ssf). Deci p k nu va mai trimite mesaje cu timestamp-ul mai mare decât T'. Dar aceasta înseamnă că p k nu va primi mesajul <m,T,j> de la p j
contrazicând corectitudinea sistemului ssf.

Total Ordering: Presupunem că pi execută bc – recvi(m1,j1,to) şi apoi


bc – recvi(m2,j2,to). Trebuie să arătăm că (T1,j1)< (T2,j2) unde Ti este timestamp-ul lui mi, (i=1,2).

Cazul I: <m2,T2,j2> este în pendingi atunci când bc – recv i(m1,j1,to) apare. Atunci, clar, (T1,j1)< (T2,j2) întrucât, altfel, m2 ar fi fost acceptat înaintea lui m1.

Cazul II: <m2,T2,j2> nu este în pendingi atunci când bc – recv i(m1,j1,to) apare.
Avem T1  tsi[j2]. Deci pi a primit un mesaj m de la p j2 (pe sistemul ssf) înaintea acestui moment cu timestamp-ul T  T1. Din proprietatea ssf, p j2 trimite m2 după ce el trimite m
şi deci timestamp-ul T2 al lui m2 este mai mare decât T. Deci T2 > T1.

Cauzalitate
Să observăm că timestamp-ul asignat unui mesaj m difuzat de pi este strict mai mare decât timestamp-ul oricărui mesaj primit de pi după primirea mesajului m. Deci:

Teoremă: Algoritmul precedent este un algoritm de broadcast c.o.

Observaţie: Cauzalitatea se poate obţine şi fără t.o., pornind de la un sistem de broadcast basic şi folosind timestamp-urile vectoriale. O raţiune pentru aceasta, este aceea că se poate
obţine calitatea c.o. chiar în prezenţă defecţiunilor în timp ce t.o. nu poate fi asigurată în prezenţa defecţiunilor.

Reliabilitate
Vom implementa un serviciu de broasdcast reliabil în topul unui sistem cu MP point-to-point în prezenţa a f căderi. Interfaţa sistemului MP point-to-point este fomată din
evenimente de intrare sendi(M), recvj(M) unde M este o mulţime de mesaje, ficare mesaj incluzând o indicaţie a trimiţătorului şi a primitorului. Secvenţele admisibile se definesc
având următoarea proprietate (procesoarele sunt partiţionate în defecte şi nedefecte primele fiind în număr de cel mult f).

Integrity: Orice mesaj primit de un procesor pi i-a fost în prealabil trimis de


un procesor.

No duplicates: Nici un mesaj trimis nu este primit mai mult de o singură


dată.

Nonfaulty Liveness: Orice mesaj trimis de un procesor nedefect la un


procesor nedefect va fi odată şi odată primit.

Algoritmul de broadcast reliabil (codul pentru pi, 0  i  n-1)

la apariţia lui bc – sendi(m,reliable):


1: enable bc – sendi(<m,i>,basic)

la apariţia lui bc – recvi(<m,k>,j,basic):


2: if m nu a fost deja primit then
3: enable bc – sendi(<m,k>,basic)
4: enable bc – recvi(m,k,basic)

Teoremă: Algoritmul precedent este un algoritm de broadcast reliabil.

Observaţii:
1: Calitatea de ssf se obţine exact ca în cazul lipsei căderilor.
2: Broadcast-ul t.o. reliabil nu se poate obţine atunci când sistemul pe care se bazează este asincron (altfel s-ar putea rezolva problema consensului în sisteme asincrone)
3: Broadcast-ul c.o. se obţine utilizând algoritmul dat pe un sistem de broadcast reliabil.
4: Toate problemele de implementare precedente pot fi extinse pentru a considera aşa numitul multicast în care un mesaj trebuie trimis la un grup de procesoare şi nu la toate.
Implementarile sunt simple atunci când grupurile sunt statice. Atunci când grupurile se pot modifica, în fiecare execuţie apar probleme la întreţinerea dinamică a lor.

Memorie Partajată Distribuită


Memoria Partajată Distribuită (Distributed Shared Memory), DSM, este o simulare a unui sistem de comunicaţie bazat pe memorie partajată cu ajutorul unui sistem de tip
transmitere mesaje (MP).
Memorie Partajată
SIM
Sistem MP

Programul de simulare va fi numit MCS (memory consistency system) care este alcătuit din procese locale în fiecare procesor p i. Schematic:

Pentru simplitate, vom considera că obiectele din memoria partajată sunt regiştri read/write.De asemenea, nu vom considera defecţiuni ale procesoarelor.
Fiecare obiect partajat are o specificare secvenţială care indică comportarea dorită a obiectului în absenţa concurenţei.
O specificare secvenţială constă dintr-o mulţime de operaţii şi o mulţime de secvenţe de operaţii (mulţimea secvenţelor legale de operaţii).

răspuns răspuns răspuns


Invoc. Invoc. Invoc.

MCS MCS MCS


În În În
p0 p1 p n-1

receive receive receive


send send send

REŢEAUA

DSM

Exemplu: Un obiect read/write X suportă operaţiile read şi write.


Invocarea unui read este readi (X) şi răspunsul returni (X, v), unde i indică nodul şi v valoarea returnată.
Invocarea unui write este writei (X, v) şi răspunsul acki (X)
O secvenţă de operaţii este legată dacă fiecare read returnează valoarea celui mai recent write precedent (valoarea iniţială dacă această nu există).
Sistem de comunicatie Linearizable Shared Memory ( LSM)
Intrări: invocări ale obiectelor partajate.
Ieşiri: răspunsuri din obiectele partajate.

Secvenţele admisibile: O secvenţă  de intrări şi ieşiri este admisibilă dacă satisface:


Correct interaction: i I este un şir alternat de invocări şi răspunsuri
pereche, începând cu o invocare (fiecare procesor trebuie să
aştepte un răspuns de la un obiect înainte de a invoca un alt
obiect; este interzis accesul tip pipeline)
Liveness:  invocare are un răspuns pereche.

Linerizability: Există o permutare a tuturor operaţiilor din  a.î.


1. pentru orice obiect O, O este legală (adică aparţine specificarii secvenţiale a lui O)
2. dacă răspunsul la operaţia o1, apare în  înainte de invocarea operaţiei o2, atunci o1 apare înaintea lui o2 în .
(Este posibilă o reordonare a operaţiilor din secvenţa  a.î. să respecte
semantica obiectelor –exprimata de specificarile secventiale- si
ordonarea după timpul real de apariţie a evenimentelor, în toate nodurile.)
Exemplu: Fie x şi y regiştri, iniţializaţi cu 0.
Atunci 1 = write0(x, 1) write1(y, 1) ack0(x) ack1(y) read0(y) read1(x) return0(y,1) return1(x, 1) este liniarizabilă întrucât 1 = w0w1r1r0 este o permutare care satisface condiţiile din
definiţie.
Însă, 2 = write0(x, 1) write1(y, 1) ack0(x) ack1(y) read0(y) read1(x) return0(y,0) return1 (x, 1) nu-i liniarizabilă.
Pentru a respecta semantica lui y, r0 trebuie să preceadă w1. Dar aceasta nu respectă ordonarea după timpul real pentru că w1 precede r0 în 2.

Sistemul de comunicaţie memorie partajată secvenţial consistentă

Aceleaşi intrări şi ieşiri iar secvenţele admisibile  satisfac Correct interaction, Liveness şi
Sequentially consistent: Există o permutare  a operaţiilor din  a.î.
1. pentru orice obiect O, |O este legală.
2. dacă răspunsul pentru operaţia o1 în nodul pi (oarecare) apare în  înaintea invocării operaţiei o2 în nodul pi, atunci o1 apare înaintea lui o2 în .

Observaţii:
1. Se cere deci ca să se păstreze ordinea operaţiilor din acelaşi nod care nu se suprapun şi nu pentru toate operaţiile.
2. În exemplul precedent 2 este secvenţă consistentă, permutarea dorită este w0r0w1r1. Deci condiţia de liniarizare este mai tare decât condiţia de consistenţă secvenţială.
Există secvenţe care nu sunt secvenţial consistente:
3 = write0(x, 1) write1(y, 1) ack0(x) ack1(y) read0(y) read1(x) return0(y,0) return1(x,0).
Pentru a respecta semantica lui x şi y trebuie ca r 0 să preceadă w1 şi r1 să preceadă w0. Pentru a respecta ordinea evenimentelor din p 1 trebuie ca w1 să preceadă r1. Din tranzitivitate
rezultă că r0 precede w0 care încalcă ordinea evenimentelor în p0.

Algoritmi
Vom presupune că sistemul MP de bază suportă broadcast t.o. (am arătat cum poate fi implementat). Pentru a evita confuzia vom folosi:

 tbc – sendi(m) pentru bc – sendi(m,t.o.) şi

 tbc – recvi(m) pentru bc – recvi(m,t.o.)

Toţi algoritmii folosesc replicarea completă: există o copie locală a oricărui obiect partajat în starea procesului MCS din fiecare nod. (Aşa cum se face, de exemplu în ORCA;
gestionarea copiilor în alt stil este posibilă dar aceasta este o altă problemă).
Algoritm pentru simularea unui DSM liniarizabil

Atunci când procesul MCS dintr-un nod primeşte o cerere să citească sau să scrie un obiect partajat:

 MCS trimite un mesaj broadcast conţinând cererea şi aşteaptă să-şi primească mesajul său.

 După primirea mesajului, execută răspunsul pentru operaţia ce aşteaptă (returnând valoarea copiei obiectului pe care o deţine el pentru un read şi executând ack
pentru write).

 Ori de câte ori un proces MCS primeşte un mesaj difuzat pentru un write, actualizează copia obiectului, în mod corespunzător.

Teoremă: Dacă sistemul de broadcast este total ordonat atunci algoritmul precedent simulează un DSM liniarizabil.

Demonstraţie: Fie  o execuţie admisibilă a algoritmului (fair, user compliant pentru sistemul cu memorie partajata si corecta pentru sistemul broadcast de comunicaţie). Trebuie să
demonstrăm că top() este liniarizabil. Fie = top(). Ordonăm operatiile din  după ordinea totală a broadcast-urilor asociate lor din  şi obţinem o permutare .
  respectă semantica obiectelor. Fie x un obiect read/write. |x sete secvenţa operatiilor ce accesează x. Cum broadcast-urile sunt t.o., orice proces MCS primeşte mesajele pentru
operaţiile asupra lui x în aceeaşi ordine, care este ordinea lui , şi gestionează x corect.
  respectă ordonarea după timpul real de apariţie a operaţiilor în .
Presupunem că o1 se termină în  înainte ca operaţia o2 să înceapă.
Atunci broadcast-ul corespunzător lui o 1, a fost primit de iniţiatorul său înainte ca broadcast-ul lui o 2 să fie trimis de iniţiatorul său. Evident, boadcastul lui o 2 este ordonat după cel al
lui o1. Deci operaţia o1 apare în  înaintea lui o2. 

Observaţie: Cum un serviciu broadcast t.o. se poate implementa în topul unui sistem MP point-to-point, rezultă că un DSM liniarizabil se poate implementa în topul unui sistem MP.
Nu este evident de ce trebuie difuzat un mesaj pentru operaţia read, atâta timp cât nu se modifică nici o copie a obiectului ca rezultat al primirii mesajului de citire.
Considerăm algoritmul în care read-ul returnează imediat valoarea din copia procesului MCS respectiv şi renunţă la broadcast.
Fie o execuţie în care valoarea iniţială a lui x este 0. Procesorul p i invocă o scriere a lui 1 în x şi execută un tbc – send pentru write. Mesajul difuzat ajunge la procesorul p j
care-şi actualizează copia sa locală a lui x pe 1, după care, sa spunem, execută un read pe x ce returnează valoarea nouă 1. Considerăm un al treilea procesor p k care nu a primit mesajul
de broadcast al lui pi şi are încă valoarea copiei lui x pe 0. Presupunem că pk citeşte x (după ce pj execută read, dar înainte de a primi broadcastul lui pi); clar va obţine valoarea 0.
Nici o permutare a acestor trei operaţii nu poate fi şi conformă cu specificaţia lui read/write şi să păstreze ordonarea după timpul real de apariţie a operaţiilor în .
Totuşi acest algoritm îndeplineşte condiţia de consistenţă secvenţială.
Algoritm de consistenţă secvenţială cu read local
(codul pentru procesorul pi)
Iniţial, copy[x] păstrează valoarea iniţială a obiectului partajat x pentru orice x.
1: atunci când apare readi(x):
2: enable returni(x, copy[x])

3: atunci când apare writei(x,v):


4: enable tbc – sendi(x,v)

5: atunci când apare tbc – recvi(x,v) din pj :


6: copy[x]:=v
7: if j=i then enable acki(x)

Fie  o execuţie admisibilă a algoritmului.


Lema 1: Pentru orice procesor pi copiile locale ale lui pi sunt actualizate de operaţiile write în aceeaşi ordine în fiecare procesor şi această ordine păsterază ordinea operaţiilor write
asociate fiecărui procesor.
Această ordine va fi numită ordine tbcast.

Lema 2: Pentru orice procesor pi şi orice obiecte partajate x şi y dacă un read r al obiectului y urmează după un write w asupra obiectului x în top()|i atunci readul r al copiei locale
a lui pi a lui y urmează după write-ul w al lui pi asupra copiei locale a lui x.
Din cele două leme rezultă.
Teoremă: Algoritmul precedent implementează un DMS secvenţial consistent cu read local.

Demonstraţie: Trebuie demonstrat că top() satisface condiţia de consistenţă secvenţială.


Construcţia lui .
Ordonăm write-urile din  conform ordinii tbcast. Trebuie să mai inserăm read-urile. Pentru fiecare read din  parcurs de la stânga la dreapta: Read-ul r executat de pi asupra lui x este
plasat imediat după ultima apariţie din  asociată (1) apariţiei precedente din  asupra lui pi (read sau write asupra oricărui obiect) şi (2) write-ului care a cauzat ultima actualizare a
copiei locale a lui x deţinută de pi, care precede punctul în care returul lui r a fost activat.
În caz de egalitate se foloseşte id-ul procesoarelor pentru stabilirea ordinii (de exemplu dacă orice procesor citeşte un obiect inainte ca orice procesor să scrie un obiect atunci  începe
cu read-ul lui p0, urmat de read-ul lui p1 ş.a.m.d).
Trebuie demonstrat că top()|i=|i. Din construcţe, pentru un procesor p i fixat ordinea relativa a două read sau a două write este aceeaşi în top()|i ca şi în |i. Ceea ce trebuie arătat
este că r şi w sunt în această ordine şi aceasta rezultă din codul algoritmului. În plus trebuie arătat că  este legală. 

Se poate inversa rolul celor două operaţii:


Facem write-ul local şi read-ul încet (datorită tbc – send-urilor)

Algoritmul de consistenţă secvenţială cu write local


(codul pentru procesorul pi 0  i  n-1)
Iniţial, copy[x] păstrează valoarea iniţială a obiectului partajat x pentru  x şi num=0.
1: Atunci când apare readi(x):
2: if num=0 then enable returni(x,copy[x])

3: atunci când apare writei(x,v):


4: num:=num+1
5: enable tbc – sendi(x,v)
6: enable acki(x)

7: atunci când apare tbc – recvi(x,v) din pj:


8: copy[x]:=v;
9: if j=i then
10: num:=num-1
11: if num=0 and (există un read care aşteaptă pe x) then
12: enable returni(x,copy[x])
Ideea algoritmului: Când apare un write(x) în p i se trimite un mesaj de broadcast ca şi în algoritmul precedent; totuşi el este confirmat imediat. Când apare un read(x) în p i, dacă pi nu
are actualizări care aşteaptă (pe  obiect nu numai pe x) atunci returnează valoarea curentă a copiei sale a lui x. Altfel, aşteaptă până primeşte mesajul de broadcast pentru toate write-
urile pe care el le-a iniţiat şi abia atunci returnează. Aceasta se realizează menţinând un contor al tuturor write – broadcasturilor iniţiate de el şi aşteaptă până acesta este zero.
În esenţă, algoritmul implementează un pipeline pentru actualizarea write-urilor generate de acelaşi procesor.

Teoremă: Algoritmul precedent implementează un DMS secvenţial consistent cu write local.


Observaţie: Acest algoritm, ca şi precedentul nu asigură linearitatea.

Margini inferioare
Vom presupune că DSM a fost implementat în topul unui sistem de tip MP, point-to-point.
Adaptăm definiţia unei execuţii cu timpi (timed execution) pentru sistemul simulat, considerând că numai evenimentelor de intrare într-un nod li s-au asignat timpi,
celelalte evenimente din nod moştenind timpii evenimentelor de intrare în nod, care le-au declanşat.
În plus vom considera că:
– orice mesaj are o întârziere într-un interval [d-u,d], unde 0 < u  d.

Pentru un MCS fixat şi o operaţie op vom nota cu t op timpul, în cazul cel mai nefavorabil, peste toate execuţiile admisibile, necesitat de operatia op (diferenţa dintre timpul real de
apariţie a răspunsului şi timpul real de apariţie al invocarii).

Teorema 1: Pentru orice MCS secvenţial consistent care asigură două obiecte partajate read/write, t read + twrite  d.

Demonstraţie: Fie un MCS secvenţial consistent şi două obiecte partajate x şi y, iniţial 0 amândouă.
Există o execuţie admisibila 0 a lui MCS a.î.
top(0) = write0(x, 1) ack0(x) read0(y) return0(y,0)
şi write începe la timpul 0 iar read răspunde inaintea timpului d.
Presupunem, în plus, că orice mesaj trimis in 0 are intirzierea d. Rezulta ca nici un mesaj n-a fost primit de nici un nod la momentul de timp când read primeşte răspunsul.
Similar, există o execuţie admisibilă 1 a lui MCS a.î.
top(1) = write1(y, 1) ack1(y) read1(x) read1(x) return1(x,0)
write apare la timpul 0, răspunsul la read apare înaintea timpului d. Deci nici un mesaj n-a fost primit în nici un nod la momentul de timp când read primeşte răspunsul.
Vom construi o nouă execuţie  care combină comportarea lui p0 în 0 (în care p0 scrie x şi citeşte y) cu comportamentul lui p 1 în 1 (scrie y şi citeşte x). Cum amândoua
operaţiile pe fiecare procesor se termină înainte de momentul d, p 0 nu ştie despre opearaţiile lui p1 şi invers. Deci procesoarele returnează aceeaşi valoare în  pe care ele le fac în 0
(1). După timpul d în , se permite ca orice mesaj ce aşteaptă să fie livrat, dar este deja prea târziu pentru a mai afecta valorile returnate anterior. Clar, aceste valori sunt inconsistente
cu condiţia de consistenţă secvenţială.
Formal, fie i|i (i=0,1) şi i prefixul cel mai lung al lui i|i ce se termină înaintea timpului d, şi '=merge(0, 1). ' este prefixul unei execuţii admisibile . Dar top() este
exact secvenţa 3 din observaţia de după definiţia condiţiei de secvenţial consistenţă, care s-a văzut că nu-i secvenţial consistentă. 

Observaţie: Marginea inferioară obţinută este valabilă în particular şi pentru MCS liniarizabil. În acest caz se poate spune chiar mai mult.

Teorema 2: Pentru orice MCS liniarizabil care asigură un obiect partajat read/write scris de două procesoare şi citit de un al treilea

0 u/2 t u u
write 2 r(x,2)
p0 u
Demonstraţie: Fie un MCS liniarizabil şi un obiect partajat x iniţial 0 care este citit de p0 şi scris de p1 şi p2. Presupunem prin reducere la absurd ca t 
write 2.
Există o execuţie admisibilă  a lui MCS a.î.
w(x,1)
 p1
– top() = write (x, 1) ack (x) write (x, 2) ack (x) read (x) return (x,2).
: 1 1 2 2 0 0

– 1 2 w(x,2)
write (x,1) apare la timpul 0, write (x,2) apare la timpul u/2 şi read (x) apare la timpul u.
0


p 2 mesajelor în  sunt: d din p la p ; d-u din p la p şi d-u/2 pentru orice altă pereche ordonată de procesoare.
întârzierile 1 2 2 1

Fie =shift(,<0,u/2,-u/2>) adică îl dăm pe p1 înainte cu u/2 şi pe p2 înapoi cu u/2.


u/2 u
0 mesajelor din p1 sau în p2 devine d-u, întârzierea mesajelor din p2 sau în p1 deviner(x,2)
 este încă admisibilă, întrucât întârzierea d şi toate celelalte întârzieri rămân neschimbate.
p0
Dar top()=write2(x, 2) ack2(x) write1(x, 1) ack1(x) read0(x) return0(x,2). Dar aceasta încalcă liniarizabilitatea deoarece read-ul lui p0 ar trebui să returneze 1 şi nu 2. 
w(x,1)
: p1
w(x,2)
p2
Teorema 3: Pentru orice MCS care asigură un obiect partajat x citit de două procesoare şi scris de un al treilea

t u
write 4

Demonstraţie: Fie un MCS liniarizabil şi x un obiect partajat, iniţial 0 care este scris de p0 şi citit de p1 şi p2. Presupunem prin reducere la absurd că t u
write 4
Ideea demonstraţiei este aceeaşi ca în teorema precedentă, numai că este ceva mai elaborată.

Fie K   twrite/n  . Considerăm  o execuţie admisibilă a lui MCS în care toate întârzierile mesajelor sunt d-u/2 comţinând urmatoarele evenimente:

– La momentul u/4, p0 face un write0(x,1)

– La un moment intre
u
şi  4k  1 n , p face ack (x). (  4k  1 n  n  twrite şi deci write-ul lui p se termină în acest interval).
0 0 0
4 4 4 4
n
– La momentul 2i , p1 face read1(x) 0  i  2k.
4
n
– La un moment între 2i şi  4i  1 n , p face return (x,V ) 0  i  2k.
1 1 2i
4 4
n
– La momentul 2i , p2 face read2(x) 0  i  2k.
4

– La un moment între  2i  1 n şi  2i  2 n , p face return (x,V


2 2 2i+1 ) 0  i  2k.
4 4

Deci în top(), read-ul lui p1 al lui V0 precede write-ul lui p 0, read-ul lui p2 al lui V4k+1 urmează după write-ul lui p0, nu avem read-uri suprapuse şi ordinea citirilor valorilor din x este
V0, V1, V2,...,V4k+1.
Din liniarizabilitatea v0=0 şi V4k+1=1.  j 0  j  4k a.î. Vj=0 şi Vj+1=1. Presupunem că j este par deci Vj este rezultatul unui read al lui p1.
Definim =shift(,<0,0,-n/4>) adică îl dăm înapoi pe p 2 cu n/4.  este admisibilă, întrucât întârzierea mesajelor în p2 devine d-n, întârzierea mesajelor din p 2 devine d şi
celelalte întârzieri rămân neschimbate.

Rezultatul shift-ării este că s-a schimbat ordinea operaţiilor read în raport cu


v1, v0, v3, v2,...,vj+1, vj,..... Deci în top() avem vj+1=1 citit înaintea lui vj=0, care violează liniarizabilitatea.
0 u/4 2u/4 4ku/4 (4k+1)u/
p2 r(x,v1) r(x,v3) 4r(x,V4k+1)

r(x,v0) r(x,v2) r(x,V4k)


: p1

p0

w(x,1)
r(x,v1) r(x,v3) r(x,V4k+1)
p2

r(x,v0) r(x,v2) r(x,V4k)


: p1

w(x,1)
p0

Simulări tolerante la defecţiuni ale obiectelor read-write


Simularea unui obiect de nivel înalt cu unul de nivel jos este utilă pentru proiectarea algoritmilor folosind primele obiecte deşi execuţia se face într-un sistem realist ce
dispune doar de ultimele.
Vom prezenta simulări de sisteme cu memorie partajată în topul altor tipuri de obiecte cu memorie partajată sau în topul unui sistem asincron de tip transmisie de mesaje, în
prezenţa defecţiunilor de tip crash.
Considerarea căderilor se poate face în două moduri echivalente: definim un sistem de memorie partajată care să permită defecţiuni sau modificăm definiţia simulării
păstrând definiţia sistemelor de memorie partajată libere de defecţiuni.

Sistem cu memorie partajată f-rezilient


Intrările sunt invocări ale obiectelor partajate şi ieşirile sunt răspunsuri de la obiectele partajate. Se presupune o partiţie a procesoarelor cu două clase, procesoare defecte şi nedefecte,
numărul celor defecte este mai mic sau egal cu f. O secvenţă  de i|o este admisibilă dacă următoarele proprietăţi au loc:

Correct Interaction: Pentru fiecare procesor pi, |i este şir alternat de invocări şi răspunsuri pereche, începind cu o invocare.

Nonfaulty Liveness: Orice invocare a unui procesor nedefect are un răspuns pereche.

Extended Liniarizability: Există o permutare  a tuturor operaţiilor complete din  (cele care au răspuns) şi a unei submulţimi a operaţiilor incomplete a.î.:
1. |o este legală pentru orice obiect o.
2. Dacă în , răspunsul la operaţia o1 apare înaintea invocării lui o2 , atunci o1 apare înaintea lui o2 în .

Vom studia dacă sistemul de comunicaţie C1 poate simula sistemul de comunicaţie C2 , unde C2 este un sistem cu memorie partajată cu un anumit tip de obiecte care
admite f căderi de procesoare, iar C1 este un sistem de comunicaţie (sau cu memorie partajată sau de tip MP) care admite f căderi de procesoare.
De fapt vom discuta cazul f=n-1, numit wait-free datorită definiţiei următoare echivalente.

O simulare a unui sistem de tip memorie partajată (fără defecţiuni) de către alt sistem de tip memorie partajată (tot fără defecţiuni) se numeşte wait-free dacă:
Pentru orice execuţie admisibilă  a programului de simulare, daca ' este un prefix în care un procesor p i are o operaţie incompletă (există în ' o invocare din
mediul extern în pi fără un răspuns pereche) =', atunci |pi conţine răspunsul la această operaţie.
(Orice operaţie trebuie să fie capabilă să se termine în orice punct fără asistenţa altor procesoare, f=n-1; operaţiile pe procesoarele nedefecte se desfăşoară normal).
Observaţie: Condiţia de liniarizabilitate corespunde intuiţiei că operaţiile (de nivel înalt) apar ca fiind executate atomic într-un anumit moment în timpul execuţiei lor. Acest
moment se află undeva între invocarea şi răspunsul la operaţie şi se numeşte punct de liniarizabilitate. Un mod de a demonstra că o secvenţă este liniarizabilă este acela de a
asigna explicit fiecărei operaţii un punct de liniarizabilitate. Acesta va asigura ordinea temporală a operaţiilor ce nu se suprapun. Dacă se arată în plus că fiecare nod
returnează valoarea operatorului write cu punctul de liniarizabilitate cel mai recent ce precede operatorul read, am demonstrat că execuţia este liniarizabilă.
Vom prezenta în continuare următorul lanţ de simulări wait-free ale registrelor de tip read-write.

single writer single writer single writer multi-writer


single reader single reader multi reader multi reader
binary multi-valued multi valued multi-valued
1. Binar – Multivaluat

Un registru este single – reader (s – r) dacă există un singur procesor (cititorul) ce poate citi din el şi single – writer dacă există un singur procesor (scriitorul) ce
poate scrie în el, (s – w).
Simularea unui registru cu K valori, de tip s – r, s – w cu ajutorul unor registre s – r, s – w cu valori binare.
Pentru simplitate vom considera un singur registru R cu K valori şi două procesoare bine definite scriiorul şi cititorul. Pentru simularea lui R, vom considera un tablou de K
regiştri binari B[0..K-1]. Valoarea i va fi reprezentată de un 1 în componenta B[i] şi 0 în celelalte. Deci, valorile posibile ale lui R sunt {0,1,...,K-1}.
O operaţie read(R) va fi simulată prin scanarea vectorului B începând cu indicele 0 şi returnând prima componentă egală cu 1 (dacă există). O operaţie
write(R,v) va fi simulată punând 1 în B[v] şi şi ştergând componenta egală cu 1 ce corespunde valorii precedente a lui R.
Acest scenariu simplu funcţionează în cazul operaţiilor R şi W care nu se suprapun.
Dacă operaţiile cititorului sunt în ordine read(R,2) read(R,1)iar ale scriitorului write(R,1) write(R,2) atunci, o liniarizare a acestor operaţii va trebui să se
păstreze ordinea operaţiilor fiecărui procesor. Pentru a fi îndeplinit semantica registrului R va trebui ca write(R ,1) să apară înainte de read(R,2) pe cînd write(R,1) trebuie
să apară înainte de read(R,1) şi după read(R,2). Deci, sau ordinea operaţiilor read sau ordinea operaţiilor write trebuie schimbată. Algoritmul următor rezolvă această
problemă a inversiunilor "nou – vechi" ale valorilor.

Algoritmul de liniarizare registru multi-valuat cu registre binare


Iniţial regiştrii partajati B[0].....B[K-1] sunt 0 cu excepţia lui B[i]=1, unde i este valoarea iniţială a lui R

când apare read(R): //cititorul citeşte din registrul R


1: i:=0
2: while B[i]=0 do i:=i+1
3: up,v:=i
4: for i:=up-1 downto 0 do
5: if B[i]=1 then v:=i
6: return(R,v)
când apare write(R,v): //scriitorul scrie valoarea v în registrul R
1: B[v]:=1
2: for i:=v-1 downto 0 do B[i]:=0
3: ack(R)

Evident , algoritmul este wait-free; fiecare write execută cel mult K operaţii write de nivel jos iar fiecare read execută cel mult 2K-1 operaţii read de nivel jos.
Pentru a demonstra că algoritmul este corect trebuie să arătăm că orice execuţie edmisibilă este liniarizabilă, admite o permutare a operaţiilor de nivel înalt a.î. să se
păstreze ordinea operaţiilor care nu se suprapun şi în care orice read returnează valoarea ultimului write precedent.
Fixăm o execuţie  admisibilă a algoritmului. Cum  este corectă pentru sistemul de comunicaţie cu registre binare, operaţiile cu registre binare sunt liniarizabile. Deci
fiecare operaţie de nivel jos admite un punct de liniarizabilitate.
Asociem fiecărei configuraţii a execuţiei  o "valoare actuală" a registrului multivaluat R: valoarea actuală a lui R este
k  min i | i   0,1, , K  1 , B i   1, B 0   B i  1  0
În această definiţie valoarea lui B(i) este în raport cu punctele de liniarizabilitate ale operaţiilor de nivel jos.
Construim o liniarizare a lui . Punctele de liniarizabilitate al operaţiei write(R,k) este imediat după prima configuraţie din intervalul ei în care b[k]=1 şi B[0]=...=B[k-1]=0. La
terminare alui write(R,k) (atunci când apare ack(R)) aceasta este situaţia, dar punctul de liniarizabilitate poate fi mai înainte (dacă, de exemplu valoarea precedentă a lui R a fost mai
mare decât k, el este imediat după execuţia operaţiei de nivel jos B[k]:=1).
Punctul de liniarizabilitate a lui read(R) care se termină cu return(R,k) este imediat după prima configuraţie din intervalul ei, în care k este asignat variabilei locale v, (linia 3 sau 5).
Lema 1: Valoarea actuală a lui R rămâne fixă între două puncte de liniarizabilitate ale operaţiei write consecutive de nivel înalt.
Lema 2: Pentru fiecare operaţie read r (de nivel înalt) din 2, r returnează valoarea scrisă în R de operaţia write (de nivel înalt) cu ultimul punct de liniarizabilitate înaintea ultimului
punct de liniarizabilitate al lui r.

Din cele două leme rezultă următoarea


Teoremă. Algoritmul precedent reprezintă o simulare wait-free a unui registru K-valuat folosind K regiştri binari s-r, s-w, în care fiecare operaţie necesită O(K) operaţii de nivel jos.

2. Single – reader  Multi – reader

Mai multe procesoare (n) citesc din acelaşi registru. Vom permite existenţa unui singur scriitor pentru fiecare registru. Registrului R i se va asocia o colecţie de n registre s –
w, s – r Val[i] i=1,n , unul pentru fiecare cititor.
Un scenariu simplu de simulare:
 write(R,v) va obliga cititorul să parcurgă toate cele n registre Val[i] şi să memoreze v în ele.
 read(R) : cititorul i returnează valoarea din Val[i].

Din păcate această simulare este incorectă, există execuţii care nu sunt liniarizabile.
Exemplu:
Valoarea iniţială a registrului este 0. Scriitorul pw doreşte să scrie 1 în registru. Considerăm următoarea secvenţă de operaţii:
 pw starteaza operaţia de write(R,1), scriind 1 în Val[1]

 p1 citeşte din R şi returnează 1 din Val[1]

 p2 citeşte din R şi returnează 0 din Val[2]

 pw primeşte ack după scrierea lui 1 în Val[1], scrie 1 în Val[2] şi confirmă (ack) scrierea sa (de nivel superior).
Citirea lui p1 trebuie ordonată după scrierea lui p w şi citirea lui p 2 înaintea scrierii lui p w. Deci citirea lui p1 trebuie ordonată după citirea lui p 2.Totusi, citirea lui p1 apare înaintea citirii
lui p2.
Are loc următoarea teoremă:

Teoremă:
În orice simulare wait-free a unui registru s – w, m – r cu orice număr de registre s – r, s – w, cel puţin un cititor trebuie să scrie.

Soluţia pentru simulare va necesita ca cititorii să-şi scrie unii altora creind o ordine între ei. Înainte de a returna valoarea din read-ul său, un cititor îi va anunţa pe ceilalţi
aceasta valoare. Un cititor va citi valoarea scrisă de scriitor pentru el, dar şi valorile anunţate de ceilalţi cititori. Va alege cea mai recentă valoare pe care el a citit-o. Vom presupune că
registrele pot lua un număr nemărginit de valori. În această ipoteză, scriitorul poate asocia un număr întreg drept timestamp (a câta oară a scris) pentru fiecare valoare scrisă. În
continuare vom referi o valoare ca fiind perechea ce conţine o valoare reală şi numărul său de ordine.
Se vor folosi tablourile de registre s – r :
Val[i]: valoarea scrisă de pw pentru cititorul pi 1  i  n
Report[i,j]: Val returnată de cea mai recentă operaţie read
executată de pi; scris de pi şi citit de pj 1  i,j  n

Algoritm de simulare a unui registru m – r R cu regiştri s – r

Iniţial, Report[i,j]=Val[i]=(v0,0) 1  i,j  n (unde v0 este valoarea iniţială a


lui R)

când apare readr(R) : //cititorul r citeşte din registrul R

1: (v[0],s[0]):=Val(v) // cea mai recentă valoare raportată lui pr , de scriitor.


2: for i:=1 to n do
3: (v[i],s[i]):=Report[i,r] // cea mai recentă valoare raportată lui pr , de pi
4: fie j a. î. s[j]=max(s[0],s[1],...,s[n])
5: for i:=1 to n do
6: Report[r,i]:= (v[j],s[j]) // pr raportează fiecarui cititor pi
7: returnr(R,r[j])

când apare write(R,v) : //scriitorul scrie în registrul R


8: seq:=seq+1
9: for i:= to n do
10: Val[i]:=(v,seq)
11: ack(R)

Algoritmul este wait–free : fiecare operaţie simulată execută un număr fix de oparaţii de nivel jos (n pentru write şi 2n+1 pentru read).
Liniarizabilitatea se demonstreaza considerind o execuţie admisibilă  oarecare si construind explicit permutarea .

3. Single – writer  Multi – writer

Ideea algoritmului de simulare. Fiecare scriitor anunţă valoarea pe care intenţionează să o scrie, tuturor cititorilor (scriindu-le-o în registrul fiecăruia, registru de tip s – w, m
– r). Fiecare cititor citeşte toate valorile scrise de scriitori şi o alege pe cea mai recentă dintre ele. Depistarea valorii cea mai recentă se face cu ajutorul unui timestamp asociat fiecărei
valori. Spre deosebire de algoritmul precedent, timestamp-ul nu este generat de un singur procesor (unicul writer) ci de mai multe procesoare (toţi scriitorii posibili).
Scriitorii sunt p0,...,pm-1 iar cititorii sunt toate procesoarele p0,p1,...,pn-1. Se vor folosi tablourile de registri s – w, m – r :
TS[i]  timestamp-ul vectorial al scriitorului pi 0  i  m-1. Este scris de pi şi citit de toţi scriitorii.
Val[i]  Ultima valoare scrisă de scriitorul pi 0  i  m-1, împreună cu timestamp-ul vectorial asociat cu acea valoare. Scris de pi şi citit de toţi cititorii.

Algoritm de simulare a unui registru m – w R cu regiştri s – w

Iniţial, TS[i]=<0,...,0> şi
Val[i] este valoarea iniţială a lui R 0  i  m-1

când apare readr(R): // cititorul pr citeşte din registrul R, 0  r  n


1: for i:=0 to m-1 do (v[i],t[i]):=Val[i] //v,t sunt locale
2: fie j a.î. t[j]=max{t[0],...,t[m-1]} // maximul lexicografic
3: returnr(R,v[j])

când apare writew(R,v): //scriitorul pw scrie v în R, 0  w  m-1


4: ts:=NewCTS() //ts este local
5: val[w]:=(v,ts)
6: ackw(R)

procedure NewCTSw() //scriitorul pw obţine un nou timestamp vectorial


1: for i:=1 to m do lts[i]:=TS[i].[i]
2: lts[w]:=lts[w]+1
3: TS[w]:=lts
4: return lts
Simularea regiştrilor partajaţi în sisteme de tip MP

În cazul existenţei defecţiunilor, metoda descrisă pentru simularea memoriei partajate în topul unui sistem broadcast t.o., bazată pe aşteptarea confirmării din sistemul MP,
nu mai funcţionază. Atunci când există defecţiuni, un procesor nu poate fi sigur că va primi un mesaj trimis către el. Mai mult, nu poate aştepta o confirmare de la destinatar, pentru că
acesta poate pica.Vom folosi toate procesoarele ca extra – memorie pentru a implementa fiecare registru partajat (nu numai scriitorii şi cititorii acelui registru). Desigur, valorile sunt
acompaniate de numare de ordine. Metoda pe care o vom descrie funcţionează în ipoteza că numărul procesoarelor defecte f satisface f < n/2 (se poate demonstra că nu este posibilă o
simulare a unui registru read/write în topul unui sistem asincron MP dacă n2f).
Considerăm un registru particular ce va fi simulat. Când scriitorul doreşte să scrie o valoare în acest nou registru, incrementează un număr de ordine şi trimite un mesaj
<newval> conţinând noua valoare şi numărul de ordine, tuturor procesoarelor.
Fiecare primitor actualizează o variabilă locală cu această informaţie, dacă numărul de ordine asociat este mai mare decât valoarea curentă a numărului de ordine din
variabila locală; în orice eveniment receptorul trimite un mesaj de <ack>.
n n
Odată ce scriitorul primeşte <ack> de la cel puţin
 2   1 procesoare el termină de scris. Cum n>2f   2   1  n  f şi deci avem garanţia că va termina de
   
scris.Când cititorul doreşte să citească un registru, el trimite un mesaj de tip <request> tuturor procesoarelor. Fiecare procesor trimite înapoi un mesaj de tip <value> conţinând
n
informaţia curentă pe care o deţine. Odată ce cititorul primeşte <value> de la cel puţin
 2   1 procesoare, el returnează valoarea cu cel mai mare număr de secvenţă dintre cele
 
primite.
n
Comunicarea cu
 2   1 procesoare garantează un quorum pentru fiecare operaţie read sau write. Cum în aceste quorum-uri avem majoritatea procesoarelor, va exista
 
un procesor în intersecţia oricărui quorum al unui read cu orice quorum al unui write. Acesta garantează faptul că ultima valoare scrisă va fi obţinută.
Pentru a evita problemele potenţiale legate de asincronie, şi scriitorul şi cititorul menţin o variabilă locală status [0..n-1] pentru a guverna comunicarea; status[j] conţine una
din valorile:
 not-sent: mesajul pentru cea mai recentă operaţie n-a fost încă trimis lui pj (pentru că pj n-a confirmat un mesaj anterior)

 not-acked: mesajul pentru cea mai recentă operaţie a fost trimis lui pj dar n-a fost confirmat de pj

 acked: mesajul pentru cea mai recentă operaţie a fost confirmat de pj

În plus, fiecare procesor are un numărător num-acks cu care-şi numără răspunsurile primite în timpul operaţiei curente.

Algoritm pentru simularea unui registru read/write cu MP


(codul pentru unicul scriitor)
Iniţial status[j]=acked pentru j, seq=0 şi pendinng=false

Când apare writew(v): // procesorul pw scrie în registru


1: pendinng:=true
2: seq:=seq+1
3: num-acks:=0
4: for j do
5: if status[j]=ackd then //are răspuns dat pentru operaţia precedentă
6: enable sendw<newval, (v,seq)> to pj
7: status[j]:=not-acked
8: else status:=not-sent

Când apare <ack> de la pj:


9: if not pending then status[j]:=acked //nici o operaţie în execuţie
10: else if status[j]=not-sent then //răspuns la un mesaj precedent
11: enable sendw(<newval>, (v,seq)> to pj
12: status[j]:=not-acked
13: else //răspuns la operaţia curentă
14: status[j]:=acked
15: num-acks:= num-acks+1
n
16: if num - acks     1 then //confirmare de la majoritatea
2
17: pending:=false
18: enable ackw

Algoritm pentru simularea unui registru read/write cu MP


(Codul pentru unicul cititor pi)
Iniţial status[j]=acked pentru j, seq=0 şi pendinng=false

Când apare readi():


1: pendinng:=true
2: num-acks:=0
3: for j do
4: if status[j]=acked then //are răspuns dat pentru operaţia precedentă
5: enable sendi<request> to pj
6: status[j]:=not-acked
7: else status:=not-sent

Când apare receive<value, (v,s)>


8: if not pending then status[j]:=acked //nici o operaţie în execuţie
9: else if status[j]=not-sent then //răspuns la un mesaj precedent
10: enable sendi<request> to pj
11: status[j]:=not-acked
12: else //răspuns la operaţia curentă
13: if s>seq then {val:=v; seq:=s}
14: status[j]:=acked
15: num-acks:= num-acks+1
n
16: if num - acks     1 then
2
17: pending:=false
18: enable returni(val)
Algoritm pentru simularea unui registru read/write cu MP
(codul pentru orice procesor pi 0  i  n-1, inclusiv pw şi pi)
Iniţial last=(v0,0) unde v0 este valoarea iniţială a registrului simulat //v este mai recent decât valoarea curentă

Când apare receivew<newval,(v,s)> :


1: last:=(v,s)
2: enable sendi(ack) to pw

Când apare receive i(request):


3.enable sendi(value, last) to pi

S-ar putea să vă placă și