460 likes | 598 Views
Synchroniz ácia procesov. Background. Súbežný prístup k zdieľaným dátam môže viesť k ich nekoexistencii Zabezpečenie koexistencie dát pri vzájomnej spolupráci procesov si vyžaduje zaoberať sa mechanizmami synchronizácie prístupu k zdieľaným dátam
E N D
Background • Súbežný prístup k zdieľaným dátam môže viesť k ich nekoexistencii • Zabezpečenie koexistencie dát pri vzájomnej spolupráci procesov si vyžaduje zaoberať sa mechanizmami synchronizácie prístupu k zdieľaným dátam • Predpokladajme že chceme poskytnúť riešenie problému konzument – producent ktorý zapĺňa všetky „buffers“. Môžeme to urobiť na základe počítadla celého čísla, ktoré uchováva počty plných „buffers“.Najprv je počítadlo (premenná count) nastavené na 0. Zaznamenáva prírastok od producenta, ktorý vytvorí nový bufer. Stav počítadla sa znižuje v dôsledku činnosti konzumenta, ktorý spotrebuje bufer.
Producent while (true) { /* produce an item and put in nextProduced */ while (count == BUFFER_SIZE) ; // do nothing buffer [in] = nextProduced; in = (in + 1) % BUFFER_SIZE; count++; }
Konzument while (true) { while (count == 0) ; // do nothing nextConsumed = buffer[out]; out = (out + 1) % BUFFER_SIZE; count--; /* consume the item in nextConsumed }
Súbeh • (count++) implementované môže byť nasledovneregister1 = count register1 = register1 + 1count = register1 • count-- implementované môže byť nasledovneregister2 = počítadlo register2 = register2 - 1počítadlo = register2 • Uvažujme toto vykonanie vložením “count = 5” prvotne: S0: producent vykonávaregister1 = count {register1 = 5}S1: producent vykonávaregister1 = register1 + 1 {register1 = 6} S2: konzumentvykonávaregister2 = count {register2 = 5} S3: konzumentvykonávaregister2 = register2 - 1 {register2 = 4} S4: producentvykonávacount = register1 {count = 6 } S5: consumer execute count = register2 {count = 4}
Riešenie problému kritickej sekcie 1. Vzájomné vylúčenie – Ak procesPije vykonávaný v jeho kritickej sekcii, potom žiadny iný proces nemôže vstúpiť do svojej kritickej sekcie 2.Postup – Ak žiadny proces nie je vykonávaný vo svojej kritickej sekcii a je tu viac požiadaviek od procesov na vstup do ich kritickej sekcie, potom jednému z procesov bude povolené vstúpiť do kritickej sekcie. Výber procesu, ktorý dostane prístup, nesmie trvať nekonečne dlho. 3. Spravodlivosť - Žiadny proces nesmie nekonečne dlho brániť iným procesom vstúpiť do kritickej sekcie, t.j. každý proces musí mať rovnakú šancu vstúpiť do nej. • Predpokladáme, že každý proces sa vykonáva nenulovou rýchlosťou • Neexistuje žiadny predpoklad týkajúci sa relatívnej rýchlostiNprocesov
Petersonovo riešenie • Dvojprocesorové riešenie (Two process solution) • Predpokladajme, že inštrukcie LOAD a STORE sú atomické; t.j., nesmú byť prerušené. Atomická operácia je taká operácia, ktorá nemôže byť prerušená, musí sa vykonať len celá. • Dva procesy zdieľajú dve premenné: • int turn; • Boolean flag[2] • Premenná turn indikujektorý proces je na rade pre vstup do kritickej sekcie. • Prvky poľa sú inicializované na false. Pole indikátorovflagsa používa na indikovanie toho, ak proces je pripravený vstúpiť do kritickej sekcie. Akflag[i] = true - proces Pije pripravený vstúpiť do kritickej sekcie.
Algoritmus pre procesPi do { flag[i] = TRUE; turn = j; while (flag[j] && turn == j); critical section flag[i] = FALSE; remainder section } while (TRUE);
Synchronizačný Hardware • Veľa systémov ponúka hardvérovú podporu pre kód kritickej sekcie. • Jednoprocesory – mohli by zakázať prerušenia • Práve vykonávaný programový kód (bežiaci) bol by vykonávaný bez preempcie • Všeobecne, príliš neúčinné na multiprocesorových systémoch • Operačné systémy používajúce toto nie sú široko škálovateľné • Modernéstroje ponúkajúšpeciálne atomické hardwerové inštrukcie • Atomic = non-interruptable • Buď testuj pamäťové slovo a nastav hodnotu • Alebo vymeň obsahy dvoch pamäťových slov
Riešenie problému kritickej sekcie použitím zámku do { acquire lock critical section release lock remainder section } while (TRUE);
TestAndSet Inštrukcia • Definition: boolean TestAndSet (boolean *target) { boolean rv = *target; *target = TRUE; return rv: }
Riešenie použitím TestAndSet • Zdieľaná booleovská premenná lock., inicializovaná na hodnotu false. • Riešenie: do { while ( TestAndSet (&lock )) ; // do nothing // critical section lock = FALSE; // remainder section } while (TRUE);
Swap Inštrukcia • Definícia: void Swap (boolean *a, boolean *b) { boolean temp = *a; *a = *b; *b = temp: }
Riešenie použitím Swap • Zdieľaná booleovská premenná lock inicializovaná na hodnotu FALSE; Každý proces má lokálnu booleovu premennú key • Riešenie: • do { key = TRUE; while ( key == TRUE) Swap (&lock, &key ); // critical section lock = FALSE; // remainder section } while (TRUE);
Ohraničené čakanie a vzájomné vylučovanie s TestandSet do { waiting[i] = TRUE; key = TRUE; while (waiting[i] && key) key = TestAndSet(&lock); waiting[i] = FALSE; // critical section j = (i + 1) % n; while ((j != i) && !waiting[j]) j = (j + 1) % n; if (j == i) lock = FALSE; else waiting[j] = FALSE; // remainder section } while (TRUE);
Semafory • Synchronizačné prostriedky ktoré nevyžadujú aktívne, zamestnané čakanie (does not require busy waiting) • Semafor S – je celočíselná (integer)premenná • Okrerm inicializácie je prístupná len cez dve štandardné operácie ktoré modifikujúS: wait() a signal() • Pôvodne nazývanéP() aV() • Menej komplikované • Môžu byť prístupné iba cez dvenedeliteľné (atomické) operácie • wait (S) { while S <= 0 ; // no-op S--; } • signal (S) { S++; }
Semafor ako všeobecnýsynchronizačnýprostriedok • Počítací (Counting)semafor – jeho integer hodnota môže mať neobmedzený rozsah • Binárny (Binary)semafor – integer hodnota môže mať rozsah iba medzi 0 a 1; môže byť jednoduchší z hľadiska implementácie • Semafor je tiež známy akomutex locks • Je možné implementovať počítací semafor S ako binárny semafor • Poskytuje (reprezentuje) „mutual exclusion“ – vzájomné vylučovanie implementované na báze semaforov Semaphore mutex; // initialized to 1 do { wait (mutex); // Critical Section signal (mutex); // remainder section } while (TRUE);
Implementácia semaforu • Musí garantovať že žiadne dva procesy nemôžu vykonávaťwait () a signal () na tom istom semafore v tom istom čase • Takto sa implementácia stáva problémom kritickej sekcie, kde waita signal kódsú umiestnené v kritickej sekcii. • Mohol by mať teraz aktívne čakanie v implementácii kritickej sekcie • Avšak implementačný kód je krátky • Malé aktívne čakanieak kritická sekcia jezriedkavo zamestnaná • Poznamenajme, že aplikácie môžu spotrebovať viac času v kritických sekciách a preto toto nie je dobré riešenie.
Implementácia semaforu bez aktívneho čakania • Ku každému semaforu je pričlenený front čakajúcich (procesov). Každý vstup vo fronte čakajúcich obsahuje dve dátové položky: • hodnota (typu integer) • ukazovateľ (pointer) na ďalší záznam v zozname • Dve operácie: • block (blokovať) – premiestni proces vyvolajúci operáciu do vhodného frontu čakajúcich. • wakeup (zobudiť) – premiestni jeden z procesov vo fronte čakajúcich do frontu pripravených (procesov).
Implementácia semaforu bez aktívneho čakania (Pokrač.) • Implementácia wait: wait(semaphore *S) { S->value--; if (S->value < 0) { add this process to S->list; block(); } } • Implementácia signal: signal(semaphore *S) { S->value++; if (S->value <= 0) { remove a process P from S->list; wakeup(P); } }
Uviaznutie a hladovanie • Deadlock (uviaznutie)– implementovanie semaforu s frontom čakajúcich môže vyústiť do situácie, kedy dva alebo viac procesov čakajú natrvalo na udalosť, ktorá môže byť spôsobená iba jedným z čakajúcich procesov. V prípade, že takýto stav nastane, hovoríme že procesy uviazli. Táto udalosť odpovedá vykonaniu operácie signal. • Uvažujme systém s dvoma procesmi P0 ,P1 Oba procesy majú prístup k dvom semaforomS a Qnastaveným na hodnotu 1: P0P1 wait (S); wait (Q); wait (Q); wait (S); . . signal (S); signal (Q); signal (Q); signal (S); • Predpokladajme, že P0 ,P1 vykonajú po sebe wait(Q). Keď P0 vykoná wait(Q), musí čakať, až kým P1 vykoná signal(Q). Podobne, keď P1 vykoná wait(S) musí čakať, pokiaľ P0 vykoná signal(S). Preto operácie signal nemôžu byť vykonané, procesy P0 ,P1 sú uviaznuté.
Uviaznutie a hladovanie Množina procesov sa nachádza v stave uviaznutia, ak každý proces v množine čaká na udalosť, ktorá môže byť spôsobená len nejakým procesom z danej množiny. Udalosti, s ktorými sa predovšetkým stretávame a riešime ich, sú získanie a uvoľnenie zdrojov. • Starvation (hladovanie)– nekonečné blokovanie. Ide o stav, kedy proces čaká natrvalo (nekonečne) Proces nikdy nemôže byť odstránený zo semaforového radu, v ktorom je pozastavený. • Inverzia priority (Priority Inversion)- Problém plánovania, kedy proces nižšej priority drží zámok vyžadovaný procesom vyššej priority.
Klasické problémy synchronizácie • Problém ohraničeného zásobníka (Bounded-Buffer Problem) • Problém čitateľov a pisateľov (Readers and Writers Problem) • Problém obedujúcich filozofov (Dining-Philosophers Problem)
Problém ohraničeného zásobníka • Nzásobníkov, každý je schopný držať jednu položku • Semaformutexje inicializovaný na hodnotu 1 • Semafor full je inicializovaný na hodnotu 0 • Semafor emptyje inicializovaný na hodnotu N.
Problém ohraničeného zásobníka(Pokrač.) • Štruktúra procesu „producent“ do { // produce an item in nextp wait (empty); wait (mutex); // add the item to the buffer signal (mutex); signal (full); } while (TRUE);
Problém ohraničeného zásobníka(Pokrač.) • Štruktúra procesu „konzument“ do { wait (full); wait (mutex); // remove an item from buffer to nextc signal (mutex); signal (empty); // consume the item in nextc } while (TRUE);
Problém čitatelia - pisatelia • Dátový objekt (typu súbor alebo záznam) je zdieľaný viacerými konkurenčnými procesmi • Čitatelia – iba čítajú z dátovej množiny, nevykonávajú akékoľvek „updates“ (procesy ktoré sa zaujímajú iba o čítanie) • Pisatelia – môžu aj čítať aj písať • Problém – dovoliť viacnásobným čítateľom čítať v tom istom čase. Len jeden môže mať prístup k zdieľaným dátam v tom istom čase. • Zdieľané dáta • Dátová množina (Data set) • Semafor mutexinicializovaný na hodnotu 1 • Semafor wrt inicializovaný na hodnotu 1 • Integer readcount inicializovaný na hodnotu 0
Problém čitatelia - pisatelia (Pokrač.) • Štruktúra procesu „pisateľ“ do { wait (wrt) ; // writing is performed signal (wrt) ; } while (TRUE);
Štruktúra procesu Pisateľ - Čitateľ(Pokrač.) • Štruktúra procesu „čitateľ“ do { wait (mutex) ; readcount ++ ; if (readcount == 1) wait (wrt) ; signal (mutex) // reading is performed wait (mutex) ; readcount - - ; if (readcount == 0) signal (wrt) ; signal (mutex) ; } while (TRUE);
Problém obedujúcich filozofov • Zdieľané dáta • Miska ryže (dátová množina) • Semaforchopstick [5] inicializovaný na hodnote 1
Problém obedujúcich filozofov (Pokrač.) • Štruktúra filozofa i: do { wait ( chopstick[i] ); wait ( chopStick[ (i + 1) % 5] ); // eat signal ( chopstick[i] ); signal (chopstick[ (i + 1) % 5] ); // think } while (TRUE);
Problémy so semaformi • Nesprávne použitie semaforových operácií (výmena operácií, nahradenie operácií navzájom a pod.): • signal (mutex) …. wait (mutex) • wait (mutex) … wait (mutex) • Vynechať (opomenúť) wait (mutex) alebo signal (mutex) (alebo oba). V tomto prípade je porušené vzájomné vylúčenie alebo nastane uviaznutie.
Monitory • Abstrakcia vysokej úrovne, ktorá poskytuje vhodný a efektívny mechanizmus pre synchronizáciu procesov • Len jeden proces smie byť v danom čase aktívny vo vnútri monitora. monitor monitor-name { // shared variable declarations procedure P1 (…) { …. } … procedure Pn (…) {……} Initialization code ( ….) { … } … } }
Premenné typu Condition • condition x, y; • Programátor, ktorý potrebuje vytvoriť vlastnú (na mieru šitú) synchronizačnú schému, môže definovať jednu alebo viac premenných typu condition. Jediné operácie, ktoré môžu byť vyvolané na condition premenných sú wait a signal. • x.wait () – táto operácia znamená, že proces vyvolávajúci túto operáciu je suspendovaný, až kým iný proces vyvolá doleuvedenú operáciu x.signal () • x.signal () –táto operácia obnoví presne jeden pozastavený proces- Ak žiadny proces nie je pozastavený, tak operácia signal nemá žiadny účinok, to znamená že stav x je ako keby operácia nebola nikdy vykonaná.
Riešenie k problému obedujúcich filozofov monitor DP { enum { THINKING; HUNGRY, EATING) state [5] ; condition self [5]; void pickup (int i) { state[i] = HUNGRY; test(i); if (state[i] != EATING) self [i].wait; } void putdown (int i) { state[i] = THINKING; // test left and right neighbors test((i + 4) % 5); test((i + 1) % 5); }
Riešenie k problému obedujúcich filozofov (Pokrač.) void test (int i) { if ( (state[(i + 4) % 5] != EATING) && (state[i] == HUNGRY) && (state[(i + 1) % 5] != EATING) ) { state[i] = EATING ; self[i].signal () ; } } initialization_code() { for (int i = 0; i < 5; i++) state[i] = THINKING; } }
Riešenie k problému obedujúcich filozofov (pokrač.) • Každý filozofI invokuje operáciepickup() a putdown()v nasledujúcej postupnosti: DiningPhilosophters.pickup (i); EAT DiningPhilosophers.putdown (i);
Implementácia Monitorupoužitím Semaforov • Premenné semaphore mutex; // (initially = 1) semaphore next; // (initially = 0) int next-count = 0; • Každá procedúraFbude nahradená wait(mutex); … body of F; … if (next_count > 0) signal(next) else signal(mutex); • Vzájomné vylúčenie v rámci monitoru je zabezpečené.
Implementácia Monitoru • Pre každú premennúxtypu condition máme: semaphore x_sem; // (initially = 0) int x-count = 0; • Operáciax.waitmôže byť implementovaná nasledovne: x-count++; if (next_count > 0) signal(next); else signal(mutex); wait(x_sem); x-count--;
Implementácia Monitoru • Operáciax.signal môže byť implementovaná: if (x-count > 0) { next_count++; signal(x_sem); wait(next); next_count--; }
Monitor pre pridelenie jediného zdroja monitor ResourceAllocator { boolean busy; condition x; void acquire(int time) { if (busy) x.wait(time); busy = TRUE; } void release() { busy = FALSE; x.signal(); } initialization code() { busy = FALSE; } }
Príklady Synchronizácie • Solaris • Windows XP • Linux • Pthreads
Synchronizácia Solaris-u • Implementuje rozmanité zámky pre podporu multitasking-u, multithreading-u (zahrňajúc vlákna reálneho času), a multiprocessing • Používaadaptive mutexes kvôli účinnosti pri ochrane dát pre kratšie segmenty kódu • Používa premenné typu condition a čitateľ-pisateľ zámky ktoré sú relatívne náročné na implementáciu, preto sa používajú iba na dlhšie segmenty kódu (ktoré potrebujú prístup k dátam) • Používa turnikety Turniket je frontová štruktúra obsahujúca vlákna blokované na zámku. Solaris 2 používa turnikety na poradie frontu vlákien čakajúcich na získanie buď adaptívneho mutexu alebo čitateľ-pisateľ zámku.