350 likes | 486 Views
Základy operačních systémů. Meziprocesová komunikace a synchronizace Jakub Yaghob. Základní pojmy. Race conditions v ýsledek operace závisí na plánování Vzájemné vyloučení ( mutual exclusion ) kritickou operaci provádí nejvýše jeden proces Kritická sekce
E N D
Základy operačních systémů Meziprocesová komunikace a synchronizace Jakub Yaghob
Základní pojmy • Race conditions • výsledek operace závisí na plánování • Vzájemné vyloučení (mutual exclusion) • kritickou operaci provádí nejvýše jeden proces • Kritická sekce • část programu, kde se provádí kritická operace
Podmínky • Žádné dva procesy nemohou být najednou v jejich kritické sekci • Nemohou být učiněny žádné předpoklady o rychlosti nebo počtu CPU • Žádný proces mimo kritickou sekci nesmí blokovat jiný proces • Žádný proces nesmí čekat nekonečně dlouho v jeho kritické sekci
Metody dosažení vzájemného vyloučení • Aktivní čekání (busy waiting) • spotřebovává čas procesoru • vhodnější pro předpokládané krátké doby čekání • nespotřebovává prostředky OS • rychlejší • Pasivní čekání/blokování • proces je ve stavu blokován • vhodné pro delší doby čekání • spotřebovává prostředky OS • pomalejší
Aktivní čekání • Zakázání přerušení • vhodné pro jádro OS • Zámky • nefungují!!! • Důsledné střídání • porušuje podmínku 3 • Petersonovo řešení • Instrukce TSL (Test and Set Lock) • spin-lock
Příklad se zámky int lock; void proc(void) { for (;;) { nekritická_sekce(); while (lock != 0); lock = 1; kritická_sekce(); lock = 0; } }
Příklad s důsledným střídáním int turn = 0; void p1(void) { for (;;) { while (turn != 0); kritická_sekce(); turn = 1; nekritická_sekce(); } } void p2(void) { for (;;) { while (turn != 1); kritická_sekce(); turn = 0; nekritická_sekce(); } }
Petersonovo řešení (pro 2) #define N 2 /* počet procesů */ int turn; int interested[N]; /* kdo má zájem */ void enter_region(int proc) { /* proc: kdo vstupuje */ int other; other = 1-proc; /* číslo opačného procesu */ interested[proc] = TRUE; /* mám zájem o vstup */ turn = proc; /* nastav příznak */ while (turn == proc && interested[other] == TRUE); } void leave_region(int proc) { /* proc: kdo vystupuje */ interested[proc] = FALSE;/* už odcházím */ }
Instrukce TSL enter_region: tsl R,lock; načti zámek do registru R a ; nastav zámek na 1 cmp R,#0 ; byl zámek nulový? jnz enter_region ; byl-li nenulový, znova ret ; návrat k volajícímu - vstup do ; kritické sekce leave_region: mov lock,#0 ; ulož do zámku 0 ret ; návrat k volajícímu • Nutná podpora HW • Všechny současné procesory nějakou mají • Také známo jako spin-lock
Pasivní čekání • Blokovací entity se nazývají primitiva • SLEEP/WAKEUP • nefunguje!!! • Semafory • Monitory • Zprávy • Ekvivalence primitiv
SLEEP/WAKEUP • implementovány OS • atomické operace • SLEEP uspí proces, který ho zavolá • WAKEUP probudí udaný proces • nefungují!!!!
Klasické synchronizační problémy • na alegoriích ukazují časté problémy při synchronizaci • problém producent-konzument • problém obědvajících filosofů • problém ospalého holiče
Problém producent-konzument Producent (továrna) produkuje předměty. Konzument (obchod) je spotřebovává (prodává). Mezi nimi je sklad pevné velikosti. Konzument nemá co prodávat, když je sklad prázdný, producent přestane vyrábět, když je sklad plný.
Producent/konzument se SLEEP/WAKEUP #define N 100 int count = 0; void producer(void) {int item; for (;;) { produce_item(&item); if(count==N) sleep(); enter_item(item); count++; if(count==1) wakeup(consumer); } } void consumer(void) {int item; for (;;) { if(count==0) sleep(); remove_item(&item); count--; if(count==N-1) wakeup(producer); consume_item(item); } }
Semafory • implementovány OS • čítač a fronta uspaných procesů • atomické operace DOWN/UP • dvě sémantiky chování podle možných hodnot čítače • čítač >= 0 • čítač v rozsahu celých čísel (i záporný) • binární semafor (pouze hodnoty 0 a 1)
Sémantika semaforů - 1 • DOWN • pokud je čítač > 0, sníží čítač o 1 a pokračuje dál • pokud je čítač = 0, operace DOWN se zablokuje a proces je přidán do fronty čekající na tomto semaforu • UP • pokud je fronta neprázdná, vybere libovolný proces a ten probudí za DOWN • jinak zvětší čítač o 1
Sémantika semaforů - 2 • DOWN • vždy se sníží hodnota čítače o 1 • pokud je čítač >= 0, pokračuje dál • jinak se zablokuje • UP • vždy se zvětší hodnota čítače o 1 • pokud je hodnota <= 0 a fronta je neprázdná, vybere libovolný proces a ten odblokuje
Producent/konzument se semafory #define N 100 semaphore mutex = 1; void producer(void) {int item; for(;;) { produce_item(&item); down(&empty); down(&mutex); enter_item(item); up(&mutex); up(&full); } } semaphore empty = N; semaphore full = 0; void consumer(void) {int item; for(;;) { down(&full); down(&mutex); remove_item(&item); up(&mutex); up(&empty); consume_item(item); } }
Monitory • implementovány překladačem • lze si představit jako třídu C++ • všechny proměnné privátní • funkce mohou být i veřejné • vzájemné vyloučení v jedné instanci • zajištěno synchronizací na vstupu a výstupu do/z veřejných funkcí • synchronizace implementována blokovacím primitivem OS
Podmíněné proměnné monitoru - 1 • pouze uvnitř monitoru • slouží k zablokování uvnitř monitoru • není to čítač ale je tam fronta zablokovaných procesů • operace WAIT/SIGNAL • podobné jako SLEEP/WAKEUP
Podmíněné proměnné monitoru - 2 • WAIT • zablokuje proces uvnitř monitoru a umožní vniknout dovnitř jinému procesu • SIGNAL • vybere libovolný z čekajících procesů a ten probudí • problém s probuzením • spustit probuzený a druhý uspat • probouzející okamžitě opustí monitor
Zprávy • zpráva je množina nějakých dat a informací přenášených mezi odesílatelem a příjemcem • i zpráva nulové délky nese informaci • implementováno OS • atomické operace SEND a RECEIVE • založeno na principu klient/server • vhodné pro distribuované OS
Zprávy - operace • SEND • odešle zprávu • nezablokuje se • pokud na zprávu čeká příjemce operací RECEIVE, zprávu obdrží a odblokuje se • RECEIVE • přijímá zprávu • pokud není žádná zpráva dostupná, zablokuje se
Zprávy - adresace • pomocí identifikace procesu • je možná pouze komunikace mezi dvěma procesy • schránky (mailbox) • mají svojí velikost a identifikaci • operace pracují s identifikací mailboxů • pevná velikost modifikuje funkci SEND možností zablokování při plné schránce • více procesů spolupracuje na jedné schránce
Zprávy - dostaveníčko • schránka nulové velikosti • odesílatel čeká na příjemce nebo příjemce čeká na odesílatele • po předání zprávy jsou oba procesy najednou odblokovány • randezvous
Ekvivalence primitiv • pomocí jednoho blokovacího primitiva lze implementovat jiné blokovací primitivum • většinou přímočará řešení • např. implementace monitoru semaforem • problém implementace semaforu zprávami • třetí proces – server semaforů
Problém obědvajících filosofů Pět filosofů sedí okolo kulatého stolu. Každý filosof má před sebou talíř špaget a jednu vidličku. Špagety jsou bohužel slizké a je třeba je jíst dvěma vidličkami. Život filosofa sestává z období jídla a období přemýšlení. Když dostane hlad, pokusí se vzít dvě vidličky, když se mu to podaří, nají se a vidličky odloží.
Obědvající filosofové - 2 #define N 5 void phil (int i) { for(;;) { think(); take_fork(i); take_fork((i+1)%N); eat(); put_fork(i); put_fork((i+1)%N); } }
Obědvající filosofové - 3 • funkce take_forkjeblokovací • všichni najednou zvednou svoji levou a čekají na pravou • funkce take_forksje opatrná • pokud nemohu sebrat druhou vidličku, položím první • všichni zvednou levou, podívají se doprava, položí levou, atd. • filosofové pracují ale nenají se – vyhladovění (starvation)
Problém ospalého holiče Holič má ve své oficíně křeslo na holení zákazníka a pevný počet sedaček pro čekající zákazníky. Pokud v oficíně nikdo není, holič se posadí a spí. Pokud přijde první zákazník a holič spí, probudí se a posadí si zákazníka do křesla. Pokud přijde zákazník a holič už střihá a je volné místo v čekárně, posadí se, jinak odejde.
Reálně používaná primitiva • Spin-lock • Aktivní čekání • Vhodný pro očekávané krátké doby čekání • Velmi rychlý vstup do kritické sekce • Kritická sekce • Blokovací primitivum • Vhodný pro očekávané delší doby uvnitř kritické sekce • Semafor • Spouštění a zastavování vláken, čekání na událost • OS závislá
Synchronizační primitiva – vyšší úroveň – 1 • RWL (Read-Write Lock) • Vícenásobně/paralelně READ operace (nemění obsah chráněného prostředku) • Exkluzivně WRITE operace (mění obsah) • Bariéry • Hlavní vlákno vypustí pomocná vlákna a čeká na bariéře na dokončení úkolu všech vláken
Synchronizační primitiva – vyšší úroveň – 2 • Reentrantní zámky • Několik funkcí, které používají stejnou kritickou sekci • Některé z těchto funkcí volají jiné z těchto funkcí • Zdánlivě zjednodušuje použití, reálně přináší spíše potíže • Lze odstranit vyrobením blokovacích a neblokovacích variant funkcí • Uzamčené (interlocked) operace • Atomicky prováděné jednoduché operace (přičtení, inkrement, …)
Synchronizační primitiva ve Windows • Jednotné funkce pro pasivní čekání • Atomické čekání na více primitiv, možnost OR/AND • Timeout • Každé primitivum má definováno, kdy „signalizuje“ • Semafor • Signalizuje při hodnotě čítače >0 • Event • Pouze binární hodnota • Nastavení na 1 signalizuje • Autoreset/manuální reset • Kritická sekce • Jednosměrný spojový seznam • Uzamčené operace
Synchronizační primitiva na UNIXech • OS implementuje semafor • Dnes obvykle knihovna PTHREAD • Podmíněné proměnné • Mutex • RWL • Spin-lock