Operační systémy Tomáš Hudec 7 Prostředky programovacích jazyků pro IPC Obsah: 7.1 Monitor, 7.1.1 Použití monitoru pro řízení přístupu do kritické sekce, 7.1.2 Použití monitoru pro synchronizaci, 7.1.3 Producenti a konzumenti pomocí monitoru, 7.2 Bariéra, 7.2.1 Bariéra v posixových vláknech, 7.3 Podmínková proměnná, 7.4 Opakování. Řada programovacích jazyků nabízí prostředky pro meziprocesové komunikace jako je synchronizace a řízení přístupu do kritické sekce. 7.1 Monitor Monitor je nástrojem pro vzájemného vylučování a synchronizace. Koncept monitoru vynalezl již v roce 1972 Per Brinch Hansen. O dva roky později koncept podmínkových front monitoru vylepšil Tony Hoare (plným jménem sir Charles Antony Richard Hoare). Jedná se konstrukci programovacího jazyka velmi podobnou třídě. Všechny lokální proměnné jsou přístupné pouze pomocí funkcí monitoru. Nejdůležitější vlastností monitoru je, že uvnitř něj smí být v každém okamžiku nejvýše jedno vlákno (proces). Tím je zaručeno vzájemné vylučování. Synchronizaci lze řešit pomocí podmínkových proměnných monitoru. S každou podmínkovou proměnnou je sdružena fronta pro vlákna čekající na danou podmínku. Nad podmínkovými proměnnými jsou implementovány operace condition_wait a condition_signal. Operace condition_wait zablokuje vlákno a zařadí je do příslušné fronty (dle podmínkové proměnné). Vlákno může být probuzeno operací condition_signal na příslušné podmínkové proměnné. http://fei-learn.upceucebny.cz/mod/page/view.php?id=5278 1/6
Monitor 7.1.1 Použití monitoru pro řízení přístupu do kritické sekce Jelikož monitor zaručuje vzájemné vylučování při spouštění svých funkcí, stačí deklarovat sdílená data v monitoru a umístit kritickou sekci do jeho funkce. monitor CS { // deklarace sdílených proměnných critical_section() { // kód kritické sekce kritická sekce Kód vláken: repeat CS.critical_section(); // zbytková sekce forever // kritická sekce 7.1.2 Použití monitoru pro synchronizaci Předpokládejme, že jedno vlákno má provést akci (vypočítat hodnotu sdílené proměnné), na kterou musí jiné vlákno počkat (použití vypočtené hodnoty). Pro synchronizaci lze použít podmínkové proměnné monitoru v kombinaci s proměnnou vypovídající o stavu dokončení akce. http://fei-learn.upceucebny.cz/mod/page/view.php?id=5278 2/6
monitor sync { bool done = false; // proměnná určující stav akce cond_t condition; // deklarace podmínkové proměnné finished() { // zavolá se po vykonání akce done = true; condition_signal(condition); await() { // zavolá se při čekání na dokončení akce if (!done) condition_wait(condition); Kód vlákna provádějícího akci, zde vypočtení hodnoty sdílené proměnné x: x = calculate(); sync.finished(); // provedení akce // signalizace dokončení akce Kód vlákna čekajícího na dokončení akce: sync.await(); // pokud akce nebyla ještě provedena, bude čekat use(x); // použití hodnoty sdílené proměnné x vypočtené jiným vláknem 7.1.3 Producenti a konzumenti pomocí monitoru Definice problému vizte předchozí kapitolu. Stačí definovat sdílená data uvnitř monitoru (monitor zajistí vzájemné vylučování) a pro synchronizaci použít podmínkové proměnné. Funkce pro vložení a výběr položek budou součástí monitoru. http://fei-learn.upceucebny.cz/mod/page/view.php?id=5278 3/6
monitor PC { buffer[n]; // sklad: pole o kapacitě n položek in = 0; // index následující volné pozice (pro uložení producentem) out = 0; // index následující obsazené pozice (pro vyzvednutí konzumentem) count = 0; // počet položek ve skladě cond_t not_full; // signalizace volného místa ve skladě cond_t not_empty; // signalizace neprázdného skladu void append(item) { // vkládání do skladu if (count == n) // je-li sklad plný, condition_wait(not_full); // čekání na uvolnění místa kritická sekce buffer[in] = item; // uložení položky do skladu na pozici in in = (in + 1) % n; // posunutí indexu na následující místo count++; // zvýšení počtu uložených položek condition_signal(not_empty); // signalizace neprázdného skladu void take(item_t *item) { // vybírání ze skladu if (count == 0) // je-li sklad prázdný condition_wait(not_empty); // čekání na vložení položky kritická sekce *item = buffer[out]; // vyzvednutí položky ze skladu na pozici out out = (out + 1) % n; // posunutí indexu na následující místo count--; // snížení počtu uložených položek condition_signal(not_full); // signalizace volného místa ve skladě Vlákno producenta: repeat item = produce(); PC.append(item); forever // vyprodukování položky // vložení položky do skladu Vlákno konzumenta: repeat PC.take(&item); consume(item); forever // vyzvednutí položky ze skladu // zpracování položky Vzájemné vylučování je zaručeno, neboť manipulace se sdílenými daty probíhá výhradně v monitoru. 7.2 Bariéra http://fei-learn.upceucebny.cz/mod/page/view.php?id=5278 4/6
Bariéra je speciální proměnná, pomocí které lze synchronizovat skupinu vláken. Jakmile vlákno dorazí k bariéře, bude zablokováno až do okamžiku, než k bariéře dorazí daný počet vláken. Deklarace a inicializace: barrier_t barrier; // deklarace bariéry barrier_init(&barrier, 3); // stanovení počtu vláken k synchronizaci Použití bariér v jednotlivých vláknech: barrier_wait(&barrier); // čekání na daný počet vláken // nyní poběží vlákna souběžně 7.2.1 Bariéra v posixových vláknech Příklad knihovny, která podporuje bariéry, je knihovna posixových vláken. Pro použití bariér je však třeba definovat symbol _XOPEN_SOURCEna hodnotu 600 nebo vyšší ještě před vložením hlavičkového souboru posixových vláken. Deklarace a inicializace: #define _XOPEN_SOURCE 600 // před načtením pthread.h #include <pthread.h> pthread_barrier_t barrier; // deklarace bariéry int rc; // pro uložení návratového stavu rc = pthread_barrier_init(&barrier, NULL, 3); // stanovení počtu vláken k synchronizaci Pokud se má po uvolnění vláken provést nějaká jednorázová akce, lze využít návratové hodnoty čekací funkce, která vrátí právě jednomu vláknu hodnotu PTHREAD_BARRIER_SERIAL_THREADa ostatním hodnotu nula. Jiná hodnota pak znamená chybový stav. Použití bariér v jednotlivých posixových vláknech: rc = pthread_barrier_wait(&barrier); // čekání na daný počet vláken // nyní poběží vlákna souběžně switch (rc) { case PTHREAD_BARRIER_SERIAL_THREAD: // provede se v jediném (nespecifikovaném) vlákně case 0: // provede se ve všech vláknech break; default: // nastala chyba perror("pthread_barrier_wait"); 7.3 Podmínková proměnná Podmínkové proměnné umožňují vláknům čekat na nějakou událost pomocí operace condition_wait. Událost je signalizována operací condition_signal. Pokud žádné vlákno nečeká, je signál ztracen, proto se používá podmínková proměnná společně s testem, zda je třeba čekat. http://fei-learn.upceucebny.cz/mod/page/view.php?id=5278 5/6
Protože test na potřebu čekání nutně používá sdílenou proměnnou, je nutné zajistit vzájemné vylučování například pomocí mutexu. bool done = false; // stav dokončení cond_t cond; // synchronizační podmínková proměnná mutex_t mutex; // mutex pro zajištění vzájemné výlučnosti přístupu k proměnným Kód vlákna provádějícího akci, zde vypočtení hodnoty sdílené proměnné x: x = calculate(); // provedení akce mutex_lock(mutex); // zajištění exkluzivního přístupu done = true; // nastavení stavu dokončení condition_signal(cond); // signalizace dokončení akce mutex_unlock(mutex); // uvolnění přístupu Kód vlákna čekajícího na dokončení akce: mutex_lock(mutex); // zajištění exkluzivního přístupu if (!done) // pokud akce ještě nebyla dokončena, condition_wait(cond); // čekej na dokončení akce mutex_unlock(mutex); // uvolnění přístupu use(x); // použití hodnoty sdílené proměnné x vypočtené jiným vláknem 7.4 Opakování 1. Definujte koncept monitoru a popište jeho vlastnosti. 2. Popište řízení přístupu do kritické sekce pomocí monitoru. 3. Řešte synchronizaci vláken pomocí monitoru. 4. Popište problému svázaných producentů a konzumentů pomocí monitoru. 5. Popište bariéru a způsob jejich použití. 6. Popište podmínkovou proměnnou a synchronizaci vláken pomocí ní. Naposledy změněno: Pondělí, 7. říjen 2013, 15.20 http://fei-learn.upceucebny.cz/mod/page/view.php?id=5278 6/6