45
Sincronizare SO: Curs 09

SO Curs-09 Sincronizare

  • Upload
    others

  • View
    8

  • Download
    0

Embed Size (px)

Citation preview

Page 1: SO Curs-09 Sincronizare

Sincronizare

SO: Curs 09

Page 2: SO Curs-09 Sincronizare

Cuprins

• Nevoia de sincronizare

• Asigurarea coerenței

• Sincronizarea la procese și thread-uri

• Implementarea sincronizării• Implementarea sincronizării

• Problematica sincronizării

• Alternative la sincronizare

SO: Curs 09: Sincronizare 2

Page 3: SO Curs-09 Sincronizare

Suport de curs

• Operating Systems Concepts

– Capitolul 6 - Process Synchronization

• Modern Operating Systems

– Capitolul 2 - Processes and Threads– Capitolul 2 - Processes and Threads• Secțiunea 2.3 - Interprocess Communication

• Allen B. Downey - The Little Book of Semaphores

3SO: Curs 09: Sincronizare

Page 4: SO Curs-09 Sincronizare

Interacțiune în sistemele de calcul

• Între componente hardware

• Între procese/thread-uri

• Între user space și kernel space

• Între aplicații și hardware• Între aplicații și hardware

• Metode

– Notificări

– Transfer de date (e.g. message passing)

– Date partajate (e.g. shared memory)

SO: Curs 09: Sincronizare 4

Page 5: SO Curs-09 Sincronizare

Sincronizare

• Pot apărea probleme din interacțiune

– Pierdere notificări

– Date corupte

• Sincronizarea asigură• Sincronizarea asigură

– Comportament determinist

– Date coerente

• Moduri

– Secvențiere/ordonare operații (A după B)

– Acces exclusiv la resurse (doar A sau doar B)SO: Curs 09: Sincronizare 5

Page 6: SO Curs-09 Sincronizare

SINCRONIZAREA LA PROCESE ȘI THREAD-URI

6SO: Curs 09: Sincronizare

Page 7: SO Curs-09 Sincronizare

Reminder: Procese și thread-uri

Procese

• Resurse dedicate

• Spațiu de adresă propriu

• Planificabile

Thread-uri

• Partajează resursele procesului

• Pot accesa (concurent) date comune

• Planificabile

• Schimbare de context costisitoare (TLB misses)

• Single-threaded sau multi-threaded

comune

• Planificabile (cu suport la nivelul nucleului)

• Schimbări de context rapide

• exit() sau primirea unui semnal încheie întreg procesul

SO: Curs 09: Sincronizare 7

Page 8: SO Curs-09 Sincronizare

De ce procese și de ce thread-uri?

Procese

• Izolare: un proces “stricat” nu “strică” pe altul

• Programare facilă

Thread-uri

• Comunicare rapidă

• Aplicații pentru sisteme multi-core

• De obicei, nu necesită sincronizare

SO: Curs 09: Sincronizare 8

Page 9: SO Curs-09 Sincronizare

Când folosim thread-uri

• Când e neapărat nevoie

• În medii ce impun folosirea thread-urilor

– Java

– kernel development– kernel development

– OpenMP

• Pentru HPC

• Multi-core programming

– CPU intensive: libx264 (encoding), comprimare, criptare

• Nu pentru perfomanță: event I/O vs. threaded I/O

• De minimizat cazurile de folosire a thread-urilorSO: Curs 09: Sincronizare 9

Page 10: SO Curs-09 Sincronizare

Sincronizarea proceselor

• În general implicită

– sockeți

– message passing, message queues

• Explicită• Explicită

– mai rar primitive de sincronizare “clasice” (mutex-uri, semafoare)

– memorie partajată (acces exclusiv)

– semnale (secvențiere)

– API de lucru cu procese: wait

SO: Curs 09: Sincronizare 10

Page 11: SO Curs-09 Sincronizare

Sincronizarea thread-urilor

• În general explicită

• Date partajate, concurență: acces exclusiv

– mutex-uri

– spinlock-uri– spinlock-uri

• Operații secvențiale: ordonare operații

– semafoare

– variabile condiție

– bariere

– monitoare

– evenimente

– cozi de așteptare SO: Curs 09: Sincronizare 11

Page 12: SO Curs-09 Sincronizare

ASIGURAREA COERENȚEI DATELOR

12SO: Curs 09: Sincronizare

Page 13: SO Curs-09 Sincronizare

Date coerente și date corupte

• Datele coerente “au sens”, determină comportament determinist pentru aplicație

• Interacțiunea necorespunzătoare (nesincronizată) produce date incoerente(nesincronizată) produce date incoerente

• Exemplu: read before write

• Dacă un pointer a fost citit dar neinițializat ...

• Nevoie de sincronizare

13SO: Curs 09: Sincronizare

Page 14: SO Curs-09 Sincronizare

Sincronizare implicită

• Primitivele folosite nu au ca obiectiv sincronizarea

– Sincronizarea este un efect secundar (implicită)

• Transfer de date/mesaje: sockeți, pipe-uri, cozi • Transfer de date/mesaje: sockeți, pipe-uri, cozi de mesaje, MPI

• Date separate/partiționate

• Avantajos: ușor de folosit de programator

SO: Curs 09: Sincronizare 14

Page 15: SO Curs-09 Sincronizare

Transferul datelor

• Metode: read/write– read: în general blocant, așteaptă date scrise

– write: poate fi blocant dacă e bufferul plin

• Sincronizare implicită– nu există date comune, datele sunt copiate/duplicate– nu există date comune, datele sunt copiate/duplicate

– secvențiere, ordonare: read-after-write

• E nevoie de sincronizare explicită pentru mai mulți transmițători sau receptori

• Avantaje: sincronizare implicită, ușor de programat, sisteme distribuite

• Dezavantaje: overhead de comunicare, blocarea operațiilor

SO: Curs 09: Sincronizare 15

Page 16: SO Curs-09 Sincronizare

Partiționarea datelor

• Se împart datele de prelucrat

• Fiecare entitate lucrează pe date proprii

• În mod ideal nu există comunicare între entități

– realist, e nevoie de comunicare (cel puțin agregarea – realist, e nevoie de comunicare (cel puțin agregarea datelor finale)

• Avantaje: reducerea zonelor de sincronizare, programare facilă

• Dezavantaje: date duplicate, posibil overhead de partiționare și agregare, aplicabilitate redusă

SO: Curs 09: Sincronizare 16

Page 17: SO Curs-09 Sincronizare

Sincronizare explicită

• Referită doar ca “sincronizare”

• Metode/mecanisme specifice de sincronizare

• Două forme de sincronizare

– pentru date partajate: primitive de acces exclusiv– pentru date partajate: primitive de acces exclusiv

– pentru operații secvențiale: primitive de secvențiere, ordonare

• Folosită atunci când este nevoie

• Menține coerența datelor

SO: Curs 09: Sincronizare 17

Page 18: SO Curs-09 Sincronizare

Acces exclusiv

• În cazul datelor partajate

• Thread-urile/procesele concurează la accesul la date– thread-urile modifică simultan date -> date incoerente

– race condition

• Acces exclusiv• Acces exclusiv– delimitarea unei zone în care un singur thread are acces

– regiune critică (critical section)

– e vorba de date, nu cod

• Metode: lock și unlock, acquire și release

• Primitive: mutex-uri, spinlock-uri

• Avantaje: date coerente

• Dezavantaje: overhead de așteptare, cod serializatSO: Curs 09: Sincronizare 18

Page 19: SO Curs-09 Sincronizare

Atomicitate

• Accesul la date să fie realizat de un singur thread

• Accesul exclusiv se referă la metodă

• Atomicitatea este o condiție a zonei/datei protejate

• Datele/zonele neatomice partajate sunt susceptibile • Datele/zonele neatomice partajate sunt susceptibile la condiții de cursă

• Atomicitate: variabile atomice, primitive de acces exclusiv

SO: Curs 09: Sincronizare 19

Page 20: SO Curs-09 Sincronizare

Secvențierea operațiilor

• În cazul în care operațiile trebuie ordonate

• Un thread scrie, un alt thread citește– e nevoie ca un thread să aștepte după altul

• Un thread produce, alt thread consumă

• Secvențiere• Secvențiere– un thread B se blochează în așteptarea unui eveniment produs de un

thread A

– thread-ul B execută acțiunea după thread-ul A

• Metode: signal/notify și wait

• Primitive: semafoare, variabile condiție, bariere

• Avantaje: comportament determinist

• Dezavantaje: overhead de așteptareSO: Curs 09: Sincronizare 20

Page 21: SO Curs-09 Sincronizare

IMPLEMENTAREA SINCRONIZĂRII

21SO: Curs 09: Sincronizare

Page 22: SO Curs-09 Sincronizare

Cadrul pentru sincronizare

• Mai multe entități active (procese/thread-uri)

• Puncte de interacțiune (date comune, evenimente)

• Preemptivitate• Preemptivitate

22SO: Curs 09: Sincronizare

Page 23: SO Curs-09 Sincronizare

Implementare simplă

• Un singur proces/thread (doar pentru medii specializate)

• Date complet partiționate (izolare)

• Dezactivarea preemptivității• Dezactivarea preemptivității

– configurare planificator

– dezactivarea întreruperilor (întreruperii de ceas)

– doar pentru sisteme uniprocesor

• Folosirea de operații atomice

SO: Curs 09: Sincronizare 23

Page 24: SO Curs-09 Sincronizare

Implementare mutex

• Un boolean pentru starea internă (locked/unlocked)

• O coadă cu thread-urile care așteaptă eliberarea mutexului

struct mutex {

bool state;

SO: Curs 09: Sincronizare 24

queue_t *queue;

};

void lock(struct mutex *m)

{

while (m->state == LOCKED) {

add_to_queue(m->queue);

wait();

}

m->state = LOCKED;

}

void unlock(struct mutex *m)

{

m->state = UNLOCKED;

t = remove_from_queue(m->queue);

wake_up(t);

}

Page 25: SO Curs-09 Sincronizare

Nevoia de spinlock

• Funcțiile lock și unlock pe mutex trebuie să fie atomice

• Dezactivarea preemptivității

• Folosirea unui spinlock• Folosirea unui spinlock

• Spinlock-ul este o variabilă simplă cu acces atomic

• Operațiile folosesc busy waiting

• Pentru atomicitate: suport hardware

– TSL (test and set lock)

– cmpxchg (compare and exchange)

SO: Curs 09: Sincronizare 25

Page 26: SO Curs-09 Sincronizare

Implementare spinlock

• Un boolean/întreg cu acces atomic

• Atomic compare and exchange pentru lockint atomic_cmpxchg(int val, int compare, int replace)

{ /* This is an equivalent implementation. */

/* The entire operation is atomic. */

int init = val;

if (val == compare)

SO: Curs 09: Sincronizare 26

void lock(struct spinlock *s)

{

while (atomic_cmpxchg(s->val, 1, 0) == 0)

;

}

void unlock(struct spinlock *s)

{

atomic_set(s->val, 1);

}

0 – locked

1 – unlocked

if (val == compare)

val = replace;

return init;

}

Page 27: SO Curs-09 Sincronizare

Spinlock vs. mutex

Spinlock

• Busy-waiting

• Simplu

• Pentru regiuni critice scurte

Mutex

• Blocant

• Coadă de așteptare

• Pentru regiuni critice mari • Pentru regiuni critice scurte • Pentru regiuni critice mari sau în care thread-ul se blochează

SO: Curs 09: Sincronizare 27

Page 28: SO Curs-09 Sincronizare

PROBLEMATICA SINCRONIZĂRII

28SO: Curs 09: Sincronizare

Page 29: SO Curs-09 Sincronizare

Dacă nu folosim sincronizare ...

• Race conditions

– read-before-write

– write-after-write

– Time Of Check To Time Of Use

• Date corupte, incoerente

• Comportament nedeterminist

• Procesul/thread-ul poate provoca un crash (acces nevalid la memorie)

SO: Curs 09: Sincronizare 29

Page 30: SO Curs-09 Sincronizare

Dacă folosim sincronizare ...

• Implementare incorectă– deadlock

– așteptare nedefinită (pierderea notificării)

• Implementare ineficientă– granularitate regiune critică– granularitate regiune critică

– cod serial

– thundering heard

– lock contention

• Probleme implicite– overhead de așteptare

– overhead de apel

30SO: Curs 09: Sincronizare

Page 31: SO Curs-09 Sincronizare

Deadlock

• Deadly embrace

• Două sau mai multe thread-uri așteaptă simultan unul după altul

– A obține lock-ul L1 și așteaptă după lock-ul L2– A obține lock-ul L1 și așteaptă după lock-ul L2

– B obține lock-ul L2 și așteaptă după lock-ul L1

• Lock-urile trebuie luate în ordine ca să prevină deadlock

• Forma de deadlock cu spinlock-uri: livelock

SO: Curs 09: Sincronizare 31

Page 32: SO Curs-09 Sincronizare

Așteptare nedefinită

• A așteaptă un eveniment

• B nu produce evenimentul (sau A pierde notificarea)

• A așteaptă nedefinit (evenimentul nu se • A așteaptă nedefinit (evenimentul nu se produce)

SO: Curs 09: Sincronizare 32

Page 33: SO Curs-09 Sincronizare

Granularitatea regiunii critice

• Regiune critică mare

– mult cod serial, multithreading redus

– legea lui Amdahl

• Regiune critică mică• Regiune critică mică

– overhead semnificativ de lock și unlock față de lucrul efectiv în zonă

– lock contention (încărcare pe lock)

SO: Curs 09: Sincronizare 33

Page 34: SO Curs-09 Sincronizare

Thundering herd

• În momentul apelului unlock() sau notify() sunt trezite toate thread-urile

• Toate thread-urile încearcă achiziționarea lock-ului

• Doar unul reușește• Doar unul reușește

• Restul se blochează

• Operația este reluată la următorul apel unlock() sau notify()

SO: Curs 09: Sincronizare 34

Page 35: SO Curs-09 Sincronizare

Overhead de apel

• Apelurile de lock, unlock, wait, notify sunt costisitoare

• În general înseamnă apel de sistem

• De multe ori invocă planificatorul• De multe ori invocă planificatorul

• Mai multe apeluri, mai mult overhead

• Lock contention generează mai mult overhead

• De preferat, unde se poate, operații atomice

SO: Curs 09: Sincronizare 35

Page 36: SO Curs-09 Sincronizare

ALTERNATIVE LA SINCRONIZARE

36SO: Curs 09: Sincronizare

Page 37: SO Curs-09 Sincronizare

Prevenirea concurenței

• Dezactivarea preemptivității

– comportament al planificatorului

– dezactivarea întreruperilor

– doar pentru sisteme uniprocesor– doar pentru sisteme uniprocesor

• Partiționarea datelor

37SO: Curs 09: Sincronizare

Page 38: SO Curs-09 Sincronizare

Regândirea soluției

• Trecerea de la sincronizare explicită la implicită

• Folosirea de procese în loc de thread-uri

• Date partiționate/algoritmi paralelizabili• Date partiționate/algoritmi paralelizabili

– reducerea “proporției” de sincronizare

SO: Curs 09: Sincronizare 38

Page 39: SO Curs-09 Sincronizare

Lockless synchronization

• RCU (Read-Copy-Update)

• Memorie tranzacțională

• De forma Commit/Abort

SO: Curs 09: Sincronizare 39

Page 40: SO Curs-09 Sincronizare

Event-driven I/O vs. Thread I/O

Event-driven I/O

• Operații asincrone

• Un singur thread

• Automat de stări

Thread I/O

• Thread-uri per “conexiuni”

• Ușor de programat

• Necesită • Automat de stări

• Scalabilitate

• Mai dificil de programat

• Necesită sincronizare/interacțiune

SO: Curs 09: Sincronizare 40

Page 41: SO Curs-09 Sincronizare

CONCLUZII

41SO: Curs 09: Sincronizare

Page 42: SO Curs-09 Sincronizare

Când folosim thread-uri?

• Când e neapărat nevoie

• În framework-uri/medii ce impun folosirea thread-urilor

– Java

– kernel development

– OpenMP

• Pentru HPC (High Performance Computing)• Pentru HPC (High Performance Computing)

• Multi-core programming

– CPU intensive: libx264 (encoding), comprimare, criptare

• Procesele au timp de creare foarte bun și separație bună a datelor

– un proces “stricat” nu le “strică” pe celelalte

• Event-based I/O este mai scalabil

• De minimizat cazurile de folosire a thread-urilor

42SO: Curs 09: Sincronizare

Page 43: SO Curs-09 Sincronizare

Când folosim sincronizare explicită?

• Când e neapărat nevoie

• În cazul în care folosim thread-uri

• Nu am putut separa/partiționa datele

• Nu am putut folosi transfer de date/mesaje • Nu am putut folosi transfer de date/mesaje (sincronizare implicită)

SO: Curs 09: Sincronizare 43

Page 44: SO Curs-09 Sincronizare

Dacă folosim sincronizare explicită ...

• Ne referim la date (nu la cod)

• Gândim bine soluția

• Avem grijă la dimensiunea regiunilor critice

• Folosim variabile atomice unde se poate• Folosim variabile atomice unde se poate

• Pentru regiuni critice mici folosim spinlock-uri

• Pentru regiuni critice mari folosim mutexuri

• Nu folosim mai multe thread-uri decât avem nevoie

– pool de thread-uri

– boss-workers

– worker threadsSO: Curs 09: Sincronizare 44

Page 45: SO Curs-09 Sincronizare

Cuvinte cheie

• sincronizare• coerență• date partajate• acces exclusiv• atomicitate

• lock()• unlock()• signal()• wait()• deadlock• atomicitate

• date partiționate• secvențiere• preemptivitate• mutex• spinlock• cmpxchg()

• deadlock• așteptare nedefinită• granularitate• cod serial• race condition• lock contention• thundering herd• dvent-based I/O

45SO: Curs 09: Sincronizare