Wady semafora Monitory Niestrukturalny mechanizm synchronizacji Nie ułatwia wykrywania błędów synchronizacyjnych w czasie kompilacji programu Idea Jądro systemu operacyjnego jako monolityczny monitor - praca w trybie uprzywilejowanym (wzajemne wykluczanie) - wyłączny dostęp do pewnych obszarów pamięci - wyłączność wykonywania pewnych instrukcji - pośrednik w komunikacji między procesami - przydział zasobów Strukturalne typy danych w językach programowania - typy danych w Pascalu - klasy w Simuli (dane oraz procedury definujące operacje) - moduły w Moduli i pakiety w Adzie Monitor - notacja językowa (strukturalna!) dla wyrażenia - modularyzacji - abstrakcyjnych typów danych - synchronizacji Historia - Warunkowe regiony krytyczne (Hoare 72, Brinch Hansen 73) var v: shared T region v do... await W;... end Zastosowania Concurrent Pascal (Brinch Hansen 1975, systemy operacyjne Mono, Solo, Trio); Concurrent Euklid (Holt 1982, system Tunis) Monitory str. 1
Definicja monitora (składnia z Pascala C) monitor nazwa_monitora; definicje stałych i typów; deklaracje zmiennych lokalnych monitora; deklaracje lokalnych procedur i funkcji; deklaracje eksportowanych procedur i funkcji; instrukcje inicjujące monitora; Deklaracja eksportowanej procedury lub funkcji: export <deklaracja procedury lub funkcji> Jako narzędzie synchronizacji monitor zapewnia: (a) wzajemne wykluczanie działań na zmiennych dzielonych - monitor jest sekcją krytyczną - zmienne dzielone są lokalne w monitorze - dostęp do nich jest możliwy tylko przez procedury i funkcje eksportowane <nazwa monitora>.<nazwa procedury> (b) możliwość wstrzymywania i wznawiania procesów - specjalny typ condition i operacje na nim wait(zmienna typu condition) - zawiesza proces w kolejce (FIFO) związanej ze zmienną typu condition; zwalnia monitor signal(zmienna typu condition) - wznawia pierwszy proces z kolejki związanej ze zmienną typu condition; zwalnia monitor empty(zmienna typu condition): boolean - ma wartość true, jeśli żaden proces nie oczekuje w kolejce związanej ze zmienną typu condition Monitory str. 2
Uwaga: nie należy mylić wait i signal z monitora z P i V (wait i signal) dla semafora - w monitorze nie ma licznika dla zmiennej typu condition - signal do kolejki pustej jest instrukcją pustą Problem: gdzie umieszczać signal? - tylko na końcu procedury monitora (i tylko jedno) - w dowolnym miejscu procedury monitora Inne notacje: - zamiast export: *, shared, entry, lista export - monitor jako typ: var M: monitor... - parametryzacja monitorów - wait z priorytetem Interpretacja Furtki wejściowe P1 P2 P5 op op op Przykład: dostęp do zasobu Ogrodzenie monitor zasób; var zajęty: Boolean; czekamy: condition; export procedure zdobądź; export procedure zwolnij; if zajęty then wait(czekamy); zajęty := false; zajęty := true signal(czekamy) zajęty:= false Przykład: czytelnicy i pisarze Monitory str. 3 Zmienne monitora P4 Wyjście c1 c2 P3 P6 P8
monitor czytelnia; var czytelnicy: integer; {liczba czytelników w czytelni} pisanie: boolean; {czy jest pisarz w czytelni} można_pisać: condition; można_czytać: condition; export procedure chcę_czytać; if pisanie or not empty(można_pisać) then wait(można_czytać); czytelnicy := czytelnicy + 1; signal(można_czytać) {chcę_czytać} export procedure chcę_pisać; if (czytelnicy<>0) or pisanie then wait(można_pisać); pisanie := true; {chcę_pisać} export procedure koniec_czytania; czytelnicy := czytelnicy - 1; if czytelnicy = 0 then signal(można_pisać) {koniec_czytania} export procedure koniec_pisania; pisanie := false; if not empty(można_czytać) then signal(można_czytać) else signal(można_pisać) {koniec_pisania} {monitor czytelnia} czytelnicy := 0; pisanie := false {czytelnia} Monitory str. 4
process czytelnik (i: integer); czytelnia.chcę_czytać; czytam(i); czytelnia.koniec_czytania end process pisarz (i: integer); czytelnia.chcę_pisać; piszę(i); czytelnia.koniec_pisania end co pisarz(1); pisarz(2); czytelnik(1); czytelnik(2); czytelnik(3); coend Przykład: prosty system wsadowy type bufor (T) =...; var buf_we: bufor(obraz_karty); buf_wy: bufor(obraz_wiersza); process czytnik; var karta:obraz_karty; czytaj kartę z czytnika kart; buf_we.włóż (karta) end ; process wykonawca; var karta: obraz_karty; wiersz: obraz_wiersza; buf_we.pobierz (karta); przetwórz kartę i utwórz wiersz; buf_wy.włóż (wiersz) end ; process drukarka; var wiersz: obraz_wiersza; Monitory str. 5
buf_wy.pobierz (wiersz); wydrukuj wiersz na drukarce end co czytnik; wykonawca; drukarka coend. type bufor (T) = monitor var pozycje: array [0..N-1] of T; przód, tył: 0..N-1; rozmiar: 0..N; niepusty, niepełny: condition; export procedure włóż (p:t); if rozmiar = N then wait(niepełny); pozycje[tył] := p; rozmiar := rozmiar + 1; tył := (tył+1) mod N; signal(niepusty) export procedure pobierz (var t: T); if rozmiar = 0 then wait(niepusty); t := pozycje[przód]; rozmiar := rozmiar - 1; przód := (przód + 1) mod N; signal(niepełny) rozmiar := 0; przód := 0; tył := 0 Monitory str. 6
Inne mechanizmy synchronizacji w systemach ze wspólną pamięcią: numerator i licznik zdarzeń [Reed & Kanodia 1979] S: sequencer E: eventcount v: integer; {lokalne} ticket(s): przekazuje liczbę dotychczas wykonanych operacji ticket na S await(e, v): if E < v then zawieś wywołujący proces w kolejce związanej z E w oczekiwaniu na E = v advance(e): E := E + 1; odwieś proces(y) czekający na to, by E osiągnęło bieżącą wartość Problem sekcji krytycznej await (E, ticket(s) ); sekcja_krytyczna; advance (E); Problem producenta i konsumenta var biletp, biletk: sequencer; We, Wy: eventcount; bufor: array [0..N-1] of komunikat; process Producent; process Konsument; var t: integer; var u: integer; k: komunikat; k: komunikat; utwórz_komunikat (k); u := ticket(biletk); t := ticket(biletp); await(wy, u); await(we, t); await(we, u+1); await(wy, t-n+1); k:= bufor(u mod N); bufor[t mod N] := k; advance(wy); advance(we); konsumuj(k); end ; end ; Monitory str. 7