ZOS 9. cvičení, ukázky kódu Pavel Bžoch
Obsah Komunikace mezi procesy Atomické operace TSL a CAS Zámky Semafory Semafory vypsání věty Monitor Bariéra pomocí monitoru Implementace semaforu pomocí monitoru Problém producent-konzument
Komunikace mezi procesy Zasílání zpráv Každá zpráva má jednoznačné ID msgget inicializace fronty pro zprávy a spárování s identifikátorem msgsnd, msgrvc funkce pro odeslání/přijmutí zprávy Roura Příkazem pipe(int[2]) vytvoříme rouru (do parametru jsou uloženy deskriptory pro čtení a pro zápis) Potomek dědí deskriptory rodiče
Komunikace mezi procesy Sdílená paměť Mezi procesy standardně neexistuje sdílená paměť musíme si o ní explicitně říci OS ji může poskytnout, ale nemusí shmget alokace sdíleného paměťového segmentu a spárování s klíčem; vrací ID vytvořeného segmentu shmat zpřístupnění vytvořeného segmentu (namapování do adresného prostoru našeho procesu) Pro synchronizaci procesů lze použít sdílený semafor (viz dále)
Atomická operace TSL Jen pro připomenutí: atomic int TSL(int *zamek, int novahodnota) { int oldvalue = *zamek; *zamek = novahodnota; return oldvalue Celá operace musí být provedena atomicky, tj. bez přeplánování a musí být podporována hardwarem a překladačem Volání např. TSL(&zamek, 1);
Atomická operace TSL Řešení v C/C++ (překladač gcc): Funkce: int sync_lock_test_and_set(int *, int)- atomické nastavení požadované hodnoty sync_lock_release(int *) nastavení hodnoty zpět na 0 Implementace spinlocku: void tsl_spinlock() { while ( sync_lock_test_and_set(&zamek, 1) == 1);
Atomická operace TSL Řešení v javě: Balík java.util.concurrent.atomic.atomicinteger Metody: boolean compareandset(int expect, int update); void set(int newvalue); Implementace spinlocku: public void lock(){ while (! ainteger.compareandset(0, 1));
Atomická operace CAS (Compare And Swap) Další atomická operace Musí být poskytována HW počítače (stejně jako TSL) Provede atomicky test hodnoty v paměti, v případě shody nastaví novou hodnotu, vždy navrátí původní hodnotu v paměti Synchronizace bez čekání
Atomická operace CAS - definice Atomic int CAS(int* pointer,int expectedvalue, int newvalue){ int oldvalue= *pointer; if (oldvalue== expectedvalue) *pointer = newvalue; return oldvalue;
Atomická operace CAS Řešení v C/C++ (překladač gcc) Funkce: int sync_val_compare_and_swap (int *kde,int puvodni, int nove);
Atomická operace CAS Řešení v javě (java tuto operaci nepodporuje) Je nutné jí napsat pomocí jiných operací (např. synchronized metody využijeme pouze zámku, nebudeme používat metody monitoru) Implemetace: public synchronized int compareandswap (myintegervar, intexpectedvalue, intnewvalue) { int oldvalue = var.getmyintvalue(); if(oldvalue == expectedvalue) var.setmyintvalue(newvalue); return oldvalue;
Zámek (mutex, Mutual exclusion) Připomenutí: Implementace: flag (zamčeno, odemčeno), struktura pro uložení čekajících vláken Funkce: Iniciace, vstup (zamknutí), opuštění (odemknutí) Platí zde princip vlastnictví ten, kdo zamyká, musí také odemknout
Zámek Řešení v C/C++ (překladač gcc) Knihovna pthread.h (#include <pthread.h>) Proměnná pthread_mutex_t Inicializace pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; Vstup do KS: pthread_mutex_lock(&lock); Opuštění KS: pthread_mutex_unlock(&lock);
Zámek Řešení v javě Balík java.util.concurrent.locks, rozhraní Lock, třída ReentrantLock Nad instancí třídy ReentrantLock lze volat metody: lock() uzamčení KS unlock() odemknutí KS
Semafor Připomenutí: Implementace: čítač, struktura pro uložení čekajících vláken Standardní operace: Iniciace (nastavení počáteční hodnoty čítače) Vstup do semaforu: P(sem) Opuštění semaforu: V(sem)
Semafor Řešení v C/C++ (překladač gcc) Knihovna semaphore.h (#include <semaphore.h>) Proměnná sem_t Iniciace: sem_init(sem_t *, int pshared, unsigned int value) Pozn. proměnná pshared slouží jako přepínač, který udává, zda je semafor sdílen mezi vlákny (=0) nebo mezi procesy (>0) Operace p: sem_wait(sem_t *); Operace v: sem_post(sem_t *);
Semafor Řešení v javě Třída java.util.concurrent.semaphore Inicializace: při vytváření instance se konstruktoru předá inicializační hodnota Semaphore s = new Semaphore (1); Operace P: s.acquire(); Operace V: s.release();
Semafor vypsání věty Proces P1: Print( Ahoj ) Print( je ) Proces P2: Print( dnes ) Print( krásně. ) Proces P3: Print( venku ) P1,P2, P3 běží paralelně. Ošetřete SEMAFORY, aby vždy byla vypsána správná věta: Ahoj dnes je venku krásně.
Semafor, věta řešení pomocí 4 semaforů
Semafor, věta řešení pomocí 3 semaforů
Monitor Připomenutí: jazyková konstrukce, kterou může vykonávat v jednu chvíli nejvýše jedno vlákno Implementace: zámek, podmínková proměnná (struktura čekajících vláken) Standardní operace: Vstup do kritické sekce, případné uspání nad podmínkovou proměnnou (wait), případné vzbuzení nad podmínkovou proměnnou(notify), opuštění kritické sekce
Monitor Řešení v C/C++ Knihovna pthread.h (#include <pthread.h>) Podmínková proměnná: pthread_cond_t Vytváření, rušení pthread_cond_init(pthread_cond_t *, const pthread_condattr_t *) pthread_cond_t c1= PTHREAD_COND_INITIALIZER; pthread_cond_destroy (pthread_cond_t *cond) Čekání na podmínku pthread_cond_wait(pthread_cond_t*, pthread_mutex_t *) Vzbuzení vlákna pthread_cond_signal(pthread_cond_t*) pthread_cond_broadcast (pthread_cond_t*, ) + POSIXové zámky
Monitor v c/c++ pthread_mutex_lock(&zamek); if (!podmnika) pthread_cond_wait(&podminka, &zamek); else pthread_cond_signal(&podminka); pthread_mutex_unlock(&zamek);
Monitor Řešení v javě Do verze javy1.5 pouze synchronizedmetoda (nebo synchronizedblok) Lze volat wait(), wait(longtimeout) pro uspání vlákna nad podmínkovou proměnnou (object) Lze volat notify(), notifyall() pro probuzení vlákna(-en) nad podmínkou proměnnou (object) Nelze např. definovat více podmínkových proměnných Viz příklad zavoznik
Monitor v javě(zámek, podmínka) Od verze 1.5 balík java.util.concurrent.locks, konkrétně třídy Condition a Lock(případně ReentrantLock) Od zámku lze získat podmínkové proměnné Doporučené řešení od Oracle(místo synchronized) Lockzamek= newreentrantlock(); Conditionpodminka1 = zamek. newcondition(); Conditionpodminka2 = zamek. newcondition();
Monitor v javě(zámek, podmínka) Nad instancí třídy Conditionlze pro uspání volat metody await(), awaituninterruptibly(), awaitnanos(longnanostimeout) Pro probuzení lze volat metody signal(), signalall() Příklad na dalším slidu, příklad zavoznik
Monitor v javě(zámek, podmínka) try { lock.lock(); //kritickasekce if (!podmnika) podminka1.awaituninterruptibly(); else podminka1.signal(); finally { lock.unlock();
Bariéra Bariéra je nastavená na N vláken Vlákna volají barrier() N-1 vláken se zde zablokuje Když přijde N-té vlákno, všechna vlákna jsou vzbuzena a projdou bariérou Např. iterační výpočty
Bariéra v c/c++ pthread_mutex_t bariera_zamek = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond_bariera = PTHREAD_COND_INITIALIZER; int bariera_cnt = 0; void barrier() { pthread_mutex_lock(&bariera_zamek); bariera_cnt++; if (bariera_cnt < N) pthread_cond_wait(&cond_bariera, &bariera_zamek) else { bariera_cnt = 0; pthread_cond_broadcast(&cond_bariera); pthread_mutex_unlock(&bariera_zamek);
Bariéra v javě výhodné použití synchronized metody private synchronized void barrier() { cnt++; if ( cnt < N) try { wait(); catch (InterruptedException ex) { else { cnt = 0; notifyall();
Implementace semaforu pomocí monitoru Obecné řešení (využívá test velikosti fronty): monitor monsem { int sem; condition c1; void P() { if sem > 0 sem=sem-1; else wait(c1); void V() { if not empty(c1) signal(c1); else sem=sem+1; void monini(int param) { sem = param; init {sem = 1;
Implementace semaforu pomocí monitoru Nevýhoda předchozího řešení: potřebujeme funkci empty(), která zjistí zda je fronta nad podmínkou prázdná Další možná implementace na dalším slidu (v javě) Praktická ukázka v C/C++
Implementace semaforu pomocí monitoru java 1/2 class Semafor{ int citac; Semafor(int citac){ this.citac = citac; public sychronized void P(){ //kód na dalším slidu public sychronized void V(){ //kód na dalším slidu
Implementace semaforu pomocí monitoru java 2/2 public sychronized void P(){ if (citac == 0) wait(); citac--; public sychronized void V(){ citac++; notify(); operace P() i V() zde vždy manipulují s proměnnou citac hodnota sem bude vždy O, 1,
Problém producent-konzument, semaforem Buffer velikosti N; semaphore prod = N; semaphore konz = 0; semaphore zamek = 1; producent() { while(1) { Produkuj(); P(prod); P(zamek); Vloz_do_buff(); V(zamek);V(konz); konzument() { while(1) { P(konz); P(zamek); Vyber_z_buff(); V(zamek); V(prod); Konzumuj(); cobegin producent() konzument() coend
Problém producent-konzument Ostatní synchronizační primitiva viz příklady v C/C++ Mimo jiné ukázka, jak pomocí zámku vyrobit semafor