Systemy operacyjne. Studia podyplomowe Wydział Informatyki PB

Podobne dokumenty
Wykład 6. Planowanie (szeregowanie) procesów (ang. process scheduling) Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Wykład 4. Synchronizacja procesów (i wątków) cześć I. Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Wykład 4. Synchronizacja procesów (i wątków) cześć I. Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Wykład 5. Synchronizacja (część II) Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Systemy operacyjne. Studia podyplomowe Wydział Informatyki PB

Programowanie równoległe i rozproszone. Monitory i zmienne warunku. Krzysztof Banaś Programowanie równoległe i rozproszone 1

Semafor nie jest mechanizmem strukturalnym. Aplikacje pisane z użyciem semaforów są podatne na błędy. Np. brak operacji sem_post blokuje aplikację.

Programowanie równoległe i rozproszone. W1. Wielowątkowość. Krzysztof Banaś Programowanie równoległe i rozproszone 1

Semafor nie jest mechanizmem strukturalnym. Aplikacje pisane z użyciem semaforów są podatne na błędy. Np. brak operacji sem_post blokuje aplikację.

synchronizacji procesów

synchronizacji procesów

Planowanie przydziału procesora

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Planowanie przydziału procesora

Programowanie współbieżne Wykład 10 Synchronizacja dostępu do współdzielonych zasobów. Iwona Kochańska

Współbieżność i równoległość w środowiskach obiektowych. Krzysztof Banaś Obliczenia równoległe 1

Ingerencja w kod systemu operacyjnego (przerwania) Programowanie na niskim poziomie (instrukcje specjalne) Trudności implementacyjne (alg.

Obliczenia równoległe i rozproszone w JAVIE. Michał Kozłowski 30 listopada 2003

Systemy operacyjne. Zajęcia 11. Monitory

Planowanie przydziału procesora CPU scheduling. Koncepcja szeregowania. Planista przydziału procesora (planista krótkoterminowy) CPU Scheduler

Zarządzanie procesorem

Monitory. Jarosław Kuchta

Przełączanie kontekstu. Planista średnioterminowy. Diagram kolejek. Kolejki planowania procesów. Planiści

Przeplot. Synchronizacja procesów. Cel i metody synchronizacji procesów. Wątki współbieżne

Zarządzanie procesami i wątkami

Języki i Techniki Programowania II. Wykład 7. Współbieżność 1

Przetwarzanie wielowątkowe przetwarzanie współbieżne. Krzysztof Banaś Obliczenia równoległe 1

Proces z sekcją krytyczną. Synchronizacja procesów. Synchronizacja procesów, cd. Synchronizacja procesów, cd. Synchronizacja procesów, cd

procesów Współbieżność i synchronizacja procesów Wykład prowadzą: Jerzy Brzeziński Dariusz Wawrzyniak

Fazy procesora i wejścia-wyjścia. Planowanie przydziału procesora. Czasy faz procesora. Planowanie przydziału procesora

Planowanie przydziału procesora

Kurs programowania. Wykład 8. Wojciech Macyna

Stworzenie klasy nie jest równoznaczne z wykorzystaniem wielowątkowości. Uzyskuje się ją dopiero poprzez inicjalizację wątku.

Programowanie współbieżne Wykład 5. Rafał Skinderowicz

Język Java wątki (streszczenie)

dr inż. Grażyna KRUPIŃSKA D-10 pokój 227 WYKŁAD 12 WSTĘP DO INFORMATYKI

Wprowadzenie do programowania współbieżnego

Synchronizacja procesów

1 Wątki 1. 2 Tworzenie wątków 1. 3 Synchronizacja 3. 4 Dodatki 3. 5 Algorytmy sortowania 4

Programowanie Równoległe i Rozproszone

Współbieżność w środowisku Java

Podstawy współbieżności

Proces z sekcją krytyczną. Synchronizacja procesów. Synchronizacja procesów, cd. Synchronizacja procesów, cd. Synchronizacja procesów, cd

Wielowątkowość mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011

Planowanie przydziału procesora

Kurs programowania. Wykład 8. Wojciech Macyna. 10 maj 2017

Systemy operacyjne III

Systemy operacyjne III

Informatyka, systemy, sieci komputerowe

Ogólna koncepcja planowania. Planowanie przydziału procesora. Komponenty jądra w planowaniu. Tryb decyzji. Podejmowanie decyzji o wywłaszczeniu

Wielowątkowość. Programowanie w środowisku rozproszonym. Wykład 1.

Wątki. Definiowanie wątków jako klas potomnych Thread. Nadpisanie metody run().

Synchronizacja procesów i wątków

Wykład 3. Procesy i wątki. Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Systemowe mechanizmy synchronizacji procesów

Wątek - definicja. Wykorzystanie kilku rdzeni procesora jednocześnie Zrównoleglenie obliczeń Jednoczesna obsługa ekranu i procesu obliczeniowego

Java. Wykład. Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Programowanie wielowątkowe. Tomasz Borzyszkowski

Język Java wątki (streszczenie)

Problemy współbieżności

w odróżnieniu od procesów współdzielą przestrzeń adresową mogą komunikować się za pomocą zmiennych globalnych

Semafory. // G - globalna dla wszystkich. // Wada - aktywne oczekiwanie Test_and_Set(Li); exit when Li = 0; end loop sekcja_krytyczna(i); G := 0;

Procesy i wątki. Blok kontrolny procesu. Proces. Proces - elementy. Stan procesu

Procesy, wątki i zasoby

Proces y i y w i ąt ą ki

Systemy Operacyjne - zarządzanie procesami

4. Procesy pojęcia podstawowe

Klasyczne problemy współbieżności. Problem producenta i konsumenta Problem czytelników i pisarzy Problem pięciu filozofów

SOP2 - semafory. grudzień

Programowanie współbieżne Wykład 9 Synchronizacja dostępu do współdzielonych zasobów. Iwona Kochańska

Procesy, zasoby i wątki

Procesy, zasoby i wątki

Prezentacja systemu RTLinux

Mechanizmy komunikacji. spotkania symetryczne (język CSP) spotkania asymetryczne (Ada) przestrzenie krotek (Linda) potoki, komunikaty i kanały (Unix)

Programowanie współbieżne Wykład 7. Iwona Kochaoska

4. Procesy pojęcia podstawowe

Programowanie komputerów

Programowanie współbieżne Wykład 4. Rafał Skinderowicz

SYSTEMY OPERACYJNE WYKLAD 6 - procesy

Planowanie przydziału procesora

Procesy, zasoby i wątki

Zaawansowany kurs języka Python

Aplikacje w Javie- wykład 11 Wątki-podstawy

Programowanie współbieżne Wykład 2. Iwona Kochańska

Współbieżność w Javie

Pytania do treści wykładów:

Programowanie współbieżne Wykład 2. Rafał Skinderowicz

SYSTEMY CZASU RZECZYWISTEGO - VxWorks

Współbieżność w Javie

projektowanie systemu

SYSTEMY OPERACYJNE PROCESORÓW SYGNAŁOWYCH

Materiały pomocnicze 1

Wstęp do programowania 2

Programowanie współbieżne i rozproszone

Systemy Operacyjne synchronizacja i komunikacja procesów

Temat zajęć: Tworzenie i obsługa wątków.

Wyjątki. Streszczenie Celem wykładu jest omówienie tematyki wyjątków w Javie. Czas wykładu 45 minut.

Podstawy Informatyki Systemy operacyjne

Synchronizacja procesów

Program jest więc strukturą statyczną zapisaną na jakimś nośniku. Natomiast proces jest wykonującym się programem.

Transkrypt:

Systemy operacyjne Studia podyplomowe 2015-2016 Wydział Informatyki PB dr inż. Marcin Czajkowski materiały przygotowane przez dr inż. Wojciecha Kwedlo

Synchronizacja procesów (i wątków)

Procesy wykonują się współbieżnie. Potrzeba synchronizacji Jeżeli w 100% są izolowane od siebie, nie ma problemu. Problem, jeżeli procesy komunikują się lub korzystają ze wspólnych zasobów. Przykład: Proces A przygotowuje wyniki, a proces B drukuje je na drukarce: Jak zapewnić, aby B nie zaczął drukować przed zakończeniem przygotowania wyników przez A. Potrzeba utrzymywania wspólnych zasobów w spójnym stanie. Np. proces A dodaje element do listy (z dowiązaniami), a jednocześnie B przegląda listę, która w momencie trwania operacji dodania ma stan niespójny Potrzeba synchronizacji dotyczy także współbieżnych wątków. W kolejnych slajdach będę używał zgodnie z literaturą pojęcia proces, jednakże wszystkie przykłady oparte są na założeniu, że procesy wykonują się współbieżnie. Zatem bardziej odpowiednie byłoby użycie terminu wątki. Np. dwa wątki wywołują funkcję malloc, która przydziela pamięć.

Problem producenta-konsumenta (z ograniczonym buforem ang. bounded buffer) Jeden proces (producent) generuje (produkuje) dane a drugi (konsument) je pobiera (konsumuje). Wiele zastosowań w praktyce np. drukowanie. Jedno z rozwiązań opiera się na wykorzystaniu tablicy działającej jak bufor cykliczny, co pozwala na zamortyzowanie chwilowych różnic w szybkości producenta i konsumenta. Tę wersję problemu nazywa się problemem z ograniczonym buforem. Problem: jak zsynchronizować pracę producenta i konsumenta np. producent zapełnia bufor, konsument usiłuje pobrać element z pustego bufora. Dwie wersje: dla jednego producenta i konsumenta i wielu producentów i konsumentów. out=3 counter=8 n=16 in=11 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Stąd konsument pobierze kolejny egzemplarz Tu producent wstawi kolejny egzemplarz

Producent konsument z buforem cyklicznym Zmienne wspólne const int n; // rozmiar bufora typedef Item; Item buffer[n]; // bufor int out=0; // indeks konsumenta int in = 0; // indeks producenta counter = 0; // liczba elementów w buforze Producent umieszcza element w buforze na pozycji in Czeka, jeżeli counter==n, tzn. bufor pełny Konsument pobiera element z bufora z pozycji out Czeka, jeżeli counter==0 tzn. bufor pusty. Zmienne in oraz out zmieniane są zgodnie z regułą i=(i+1)%n Wartości kolejnych indeksów do tablicy buffer Jeżeli i==n-1 to nowei=0

Rozwiązanie (prymitywne) dla jednego producenta i konsumenta z wykorzystaniem aktywnego czekania Counter += 1; Counter -= 1; Producent Item pitm; while (1) { produce an item into pitm while (counter == n) ; buffer[in] = pitm; in = (in+1) % n; counter += 1; } Konsument Item citm; while (1) { while (counter == 0) ; citm = buffer[out]; out = (out+1) % n; counter -= 1; consume the item in citm } Dygresja: dlaczego rozwiązanie oczywiście niepoprawne dla więcej niż jednego konsumenta albo producenta? Counter jest zmienną współdzieloną przez obydwa procesy. Co się może stać gdy jednocześnie obydwa procesy spróbują ją zmienić?

Gdzie tkwi problem? Architektura RISC: ładuj do rejestru, zwiększ wartość,zapisz wynik. Niech x oznacza jest modyfikowaną zmienną counter. Przyjmijmy, że x=5 Rozważmy dwie możliwe kolejności wykonywania instrukcji poszczególnych procesów. a) Poprawna wartość 5. b) Niepoprawna wartość 4. Wybór jednej z tych wielkości niedeterministyczny. Sytuacja wyścigu (ang. race condition) a) Producent Konsument R1 <= x R1 = R1+1 R1 => x R3 <= x R3 = R3-1 R3 => x b) Producent Konsument R1 <= x R3 <= x R3 = R3-1 R1 = R1+1 R1 => x R3 => x x=5 x=5 x=5 x=4!

Wyścigi - races O warunku wyścigu mówimy gdy wynik zależy od kolejności wykonywania instrukcji procesów. W poprawnym programie nie powinno być wyścigów. Innymi słowy Uwaga: Ze względu na niedeterminizm (nigdy nie wiemy w jakiej kolejności wykonają się procesy) do błędu może (ale nie musi dojść). W związku z tym przydatność testowania do badanie poprawności programów współbieżnych jest mocno ograniczona. Nic tu nie zastąpi analizy kodu (a często mniej lub bardziej formalnych dowodów poprawności). Typowe sytuacje: błąd objawia się przeciętnie raz na trzy miesiące nieprzerwanej pracy programu. W naszym przykładzie musimy zapewnić, aby jeden proces w danej chwili mógł odwoływać się do zmiennej Counter. Innymi słowy dostęp do tej zmiennej powinien znajdować się wewnątrz sekcji krytycznej.

Problem sekcji krytycznej Chcemy, aby w jednej chwili w sekcji krytycznej mógł przebywać tylko jeden proces Założenia Proces na przemian przebywa w sekcji krytycznej albo wykonuje inne czynności Proces przebywa w sekcji krytycznej przez skończony czas. Rozwiązanie Czynności wykonywane przy wejściu do sekcji - protokół wejścia Czynności wykonywane przy wyjściu z sekcji - protokół wyjścia A wchodzi do sekcji krytycznej A opuszcza sekcj ę krytyczną Proces A Proces B B próbuje wej ść do sekcji krytycznej B wstrzymany B wchodzi do sekcji krytycznej B opuszcza sekcj ę krytyczną Czas

Warunki dla rozwiązania sekcji krytycznej Wzajemne wykluczanie. W danej chwili tylko jeden proces może być w sekcji krytycznej. Postęp Proces który nie wykonuje sekcji krytycznej nie może blokować procesów chcących wejść do sekcji. Ograniczone czekanie Proces nie może czekać na wejście do sekcji krytycznej w nieskończoność

Rozwiązania problemu sekcji krytycznej Wyłącz przerwania Nie działa w systemach wieloprocesorowych Problematyczne w systemach z ochroną Wykorzystywane do synchronizacji w obrębie jądra (zakładając jeden procesor) Rozwiązania z czekaniem aktywnym Algorytm Petersona dla dwóch procesów wymaga trzech zmiennych!!! Algorytm piekarni dla wielu procesów. Rozwiązania wykorzystujące specjalne instrukcje maszynowe np. rozkaz zamiany: XCHG rejestr, pamięć Architektura systemu musi zapewniać atomowe wykonanie instrukcji. W systemach wieloprocesorowych nie jest to trywialne

Czekanie aktywne Marnowany jest czas procesora Zmarnowany czas można by przeznaczyć na wykonanie innego procesu. Uzasadnione gdy: Czas oczekiwania stosunkowo krótki (najlepiej krótszy od czasu przełączenia kontekstu) Liczba procesów @ Liczba procesorów Przykład zastosowania jądro Linux-a w wersji SMP Funkcje typu spin_lock Alternatywą do czekania aktywnego jest przejście procesu w stan zablokowany Semafory Monitory

Semafor zliczający Zmienna całkowita S i trzy operacje: nadanie wartości początkowej, oraz Wait i Signal. Definicja klasyczna (E. Dijkstra): Wait (czekaj): while (S<=0) ; S-- Signal(sygnalizuj): S++ Operacje Wait i Signal są operacjami atomowymi Początkowa wartość S liczba wywołań operacji Wait bez wstrzymywania. Definicja klasyczna oparta jest na aktywnym czekaniu. W praktyce używa się innej definicji opartej na usypianiu procesów (M. Ben-Ari) : Wait: Jeżeli S>0, to S=S-1, w przeciwnym wypadku wstrzymaj (przełącz w stan oczekujący) wykonywanie procesu proces ten nazywany wstrzymanym przez semafor. Signal: Jeżeli są procesy wstrzymane przez semafor, to obudź jeden z nich, w przeciwnym wypadku S=S+1. Implementacja według powyższej definicji m.in. w standardzie POSIX threads. funkcje sem_init, sem_wait oraz sem_post (odpowiednik signal) funkcja sem_trywait nie wstrzymuje procesu, ale zwracająca kod błędu jeżeli proces byłby wstrzymany.

Implementacja semafora Semafor to: (a) Bieżąca wartość + (b) Lista (np. FIFO) procesów oczekujących Nieco zmodyfikowana (ale równoważna z definicją Ben Ariego) implementacja zakładamy że wartość zmiennej może być ujemna wtedy przechowuje ona liczbę wstrzymanych procesów. Zakładamy dostępność dwóch funkcji na poziomie jądra systemu: Sleep: realizuje przejście procesu Aktywny=>Oczekujący Wakeup: Oczekujący=>Gotowy Oczywiście Wait i Signal muszą być operacjami atomowymi ich wykonanie nie może być przerwane przełączeniem kontekstu do innego procesu. class Semaphore { int value; ProcessList pl; public: Semaphore(int a) {value=a;} void Wait (); void Signal (); }; Semaphore::Wait() () { value -= 1; if (value < 0) { Add(this_process,pl) Sleep (this_process); } } Semaphore::Signal () { value += 1; if (value <= 0) { Process P=Remove(P) Wakeup (P); } }

Semaphore Sem(1); Rozwiązanie sekcji problemu sekcji krytycznej przy pomocy semaforów void Process() { while (1) { Sem.Wait(); // Proces wykonuje swoją sekcję krytyczną Sem.Signal() // Proces wykonuje pozostałe czynności } } Protokół wejścia i wyjścia są trywialne, ponieważ semafory zaprojektowano jako narzędzie do rozwiązania problemu sekcji krytycznej Zmodyfikujmy warunki zadania, tak że w sekcji krytycznej może przebywać jednocześnie co najwyżej K procesów. Pytanie. Co należy zmienić w programie?

Zastosowanie semafora to zapewnienia określonej kolejności wykonywania instrukcji procesów Chcemy aby instrukcja A jednego procesu wykonała się po instrukcji B drugiego. Używamy semafora S zainicjalizowanego na zero...... S.Wait(); A;..... B; S.Signal();

Problem producent-konsument z wykorzystaniem semaforów const int n; Semaphore empty(n),full(0),mutex(1); Item buffer[n]; Producent int in = 0; Item pitem; while (1) { // produce an item into pitem empty.wait(); mutex.wait(); buffer[in] = pitem; in = (in+1) % n; mutex.signal(); full.signal(); } Konsument int out = 0; Item citem; while (1) { full.wait(); mutex.wait(); citem = buffer[out]; out = (out+1) % n; mutex.signal(); empty.signal(); // consume item from citem } Semafor mutex zapewnia wzajemne wykluczanie przy dostępie do zmiennych współdzielonych. Semafor full zlicza liczbę elementów w buforze (pełnych miejsc w tablicy). Wstrzymuje konsumenta gdy w buforze nie ma żadnego elementu. Semafor empty zlicza liczby pustych miejsc w tablicy. Wstrzymuje producenta gdy w tablicy nie ma wolnego miejsca..

Semafory binarne Zmienna może przyjmować tylko wartość zero lub jeden Operacje mają symbole WaitB, SingalB Wartość jeden oznacza, że można wejść do semafora (wykonać WaitB) Wartość zero oznacza że operacja WaitB wstrzyma proces. Mogą być prostsze w implementacji od semaforów zliczających. Implementacje Mutexy w POSIX threads. (pthread_mutex_create, phread_mutex_lock, pthread_mutex_unlock). W win32 mutexy noszą nazwę sekcji krytycznych W Javie mutex jest związany z każdym obiektem Słowo kluczowe synchronized. Więcej o Javie przy omawianiu monitorów

Blokada (Zakleszczenie, ang. deadlock) Zbiór procesów jest w stanie blokady, kiedy każdy z nich czeka na zdarzenie, które może zostać spowodowane wyłącznie przez jakiś inny proces z tego zbioru. Samochody nie mają wstecznego biegu = Brak wywłaszczeń zasobów

Przykład blokady Sekwencja instrukcji prowadząca do blokady. P 0 wykonał operacje A.Wait() Semaphore A(1),B(1); P 1 wykonał operacje B.Wait() P 0 usiłuje wykonać B.Wait() P 1 usiłuje wykonać A.Wait() P 0 czeka na zwolnienie B przez P 1 P 1 czeka na zwolnienie B przez P 0 Będą czekały w nieskończoność!!! Do blokady może (ale nie musi) dojść. Pytanie: Jak w tej sytuacji zagwarantować brak blokady? Proces P 0 A.Wait(); B.Wait();... B.Signal(); A.Signal(); Proces P 1 B.Wait(); A.Wait();... A.Signal(); B.Signal();

Zagłodzenie (ang. starvation) Proces czeka w nieskończoność, pomimo że zdarzenie na które czeka występuje. (Na zdarzenie reagują inne procesy) Przykład: Jednokierunkowe przejście dla pieszych, przez które w danej chwili może przechodzić co najwyżej jedna osoba. Osoby czekające na przejściu tworzą kolejkę. Z kolejki wybierana jest zawsze najwyższa osoba Bardzo niska osoba może czekać w nieskończoność. Zamiast kolejki priorytetowej należy użyć kolejki FIFO (wybieramy tę osobę, która zgłosiła się najwcześniej). Inny przykład: z grupy procesów gotowych planista krótkoterminowy przydziela zawsze procesor najpierw procesom profesorów a w dalszej kolejności procesom studentów. Jeżeli w systemie jest wiele procesów profesorów, to w kolejce procesów gotowych znajdzie się zawsze co najmniej jeden i proces studenta będzie czekał w nieskończoność na przydział procesora.

Problem pięciu filozofów Każdy filozof siedzi przed jednym talerzem Każdy filozof na przemian myśli i je Do jedzenia potrzebuje dwóch widelców Widelec po lewej stronie talerza. Widelec po prawej stronie talerza. W danej chwili widelec może być posiadany tylko przez jednego filozofa. Zadanie: Podaj kod dla procesu i-tego filozofa koordynujący korzystanie z widelców.

Problem czytelników i pisarzy Modyfikacja problemu sekcji krytycznej. Wprowadzamy dwie klasy procesów: czytelników i pisarzy. Współdzielony obiekt nazywany jest czytelnią. W danej chwili w czytelni może przebywać Jeden proces pisarza i żaden czytelnik. Dowolna liczba czytelników i żaden pisarz. Rozwiązanie prymitywne: Potraktować czytelnię jak obiekt wymagający wzajemnego wykluczania wszystkich typów procesów. Prymitywne, ponieważ ma bardzo słabą wydajność. Jeżeli na wejście do czytelni czeka wielu czytelników i żaden pisarz to możemy wpuścić od razu wszystkich czytelników W literaturze opisano rozwiązania: Z możliwością zagłodzenia pisarzy Z możliwością zagłodzenia czytelników Poprawne

Problem śpiącego fryzjera c Jeden proces fryzjera i wiele procesów klientów. Współdzielone zasoby: n krzeseł w poczekalni i jedno krzesło fryzjera Napisz program koordynujący pracę fryzjera i klientów

Wady semaforów Jeden z pierwszych mechanizmów synchronizacji Generalnie jest to mechanizm bardzo niskiego poziomu - trochę odpowiadający programowaniu w assemblerze. Duża podatność na błędy, trudno wykazać poprawność programu Przykład: Jeżeli zapomnimy o operacji signal, nastąpi blokada Bardziej strukturalne mechanizmy synchronizacji Regiony krytyczne Monitory

Regiony krytyczne Współdzielona zmienna v typu T jest deklarowana jako: var v: shared T Dostęp do zmiennej v wykonywany przy pomocy operacji region v when B do S B jest wyrażeniem logicznym Tak długo, jak instrukcja S się wykonuje, żaden inny proces nie może się odwołać do zmiennej v. Jeżeli wyrażenie B nie jest spełnione, to proces jest wstrzymywany do momentu jego spełnienia.

Przykład: producent-konsument z ograniczonym buforem var buffer: shared record pool: array [0..n 1] of item; count,in,out: integer end; Deklaracja zmiennej współdzielonej. region buffer when count < n do begin end; pool[in] := nextp; in:= in+1 mod n; count := count + 1; Wstawienie elementu nextp do bufora. (Producent). region buffer when count > 0 do begin nextc := pool[out]; out := out+1 mod n; count := count 1; end; Usunięcie elementu nextc z bufora (Konsument).

Idea monitora (a właściwie zmiennej warunkowej) Udostępnienie procesom operacji pozwalającej procesowi wejść w stan uśpienia (zablokowania) wait oraz operacji signal pozwalającej na uśpienie obudzonego procesu. Ale tu natrafiamy na (stary) problem wyścigów, który ilustruje poniższy przykład: if (Jeszcze_nie_bylo_zdarzenia_muszę_wykonać_wait) wait() // to zaczekam Co się stanie, jeżeli zdarzenie na które czeka proces zajdzie po instrukcji if, ale przed uśpieniem procesu? Proces zgubi zdarzenie (i być może nigdy się nie obudzi) W takim razie wykonajmy cały ten kod wewnątrz sekcji krytycznej? Ale gdy proces wykona wait() - to przejdzie w stan uśpienia nie zwalniając sekcji krytycznej. Przy próbie wejścia do sekcji przez inny proces na pewno dojdzie do blokady. Rozwiązanie: Atomowa operacja wait powodująca jednoczesne uśpienie procesu i wyjście z sekcji krytycznej

Monitory monitor mon { int foo; int bar; public void proc1( ) { } public void proc2( ) { } }; Pseudokod przypominający definicję klasy C++. Współdzielone zmienne foo oraz bar są dostępne wyłącznie z procedur monitora. Procesy synchronizują się wywołując procedury monitora (np. proc1 i proc2) Tylko jeden proces (wątek) może w danej chwili przebywać w procedurze monitora. Gwarantuje to automatycznie wzajemne wykluczanie. Mówimy że proces przebywa wewnątrz monitora.

Zmienne warunkowe (ang. condition) Problem: proces postanawia zaczekać wewnątrz monitora aż zajdzie zdarzenie sygnalizowane przez inny proces. Jeżeli proces po prostu zacznie czekać, nastąpi blokada, bo żaden inny proces nie będzie mógł wejść do monitora i zasygnalizować zdarzenia. Zmienne warunkowa (typu condition). Proces, będący wewnątrz monitora, może wykonać na niej dwie operacje. Niech deklaracja ma postać: Condition C; C.wait() Zawiesza wykonanie procesu, i jednocześnie zwalnia monitor pozwalając innym procesom wejść do monitora. C.signal() Jeżeli nie ma procesów zawieszonych przez operację wait nic się nie dzieje. W przeciwnym wypadku dokładnie jeden proces zawieszony przez operacje wait zostanie wznowiony. (od następnej instrukcji po wait). Możliwa jest trzecia operacja C.signallAll() wznawiająca wszystkie zawieszone procesy.

Przykład 1: Implementacja semafora zliczającego przy pomocy monitora monitor Semafor { int Licznik; Condition NieZero; // coś na kształt konstruktora Semafor(int i) { Licznik=i; } void wait() { if (Licznik==0) NieZero.wait(); Licznik=Licznik-1; } void signal() { Licznik=Licznik+1; NieZero.signal(); } }; Deklaracja: Semafor S(1); Proces potrzebujący wzajemnego wykluczania. S.wait() // sekcja krytyczna S.signal() // pozostałe czynności

Struktura monitora Zasada działania: W danej chwili w procedurze monitora może przebywać jeden proces. Z każdą zmienną warunkową związana jest kolejka procesów, które wywołały wait i oczekują na zasygnalizowanie operacji. Po zasygnalizowaniu warunku proces (który wykonał operację signal) przechodzi do kolejki urgent queue. Zatem implementacja realizuje semantykę Hoare'a

Przykład 2: Problem producent-konsument z buforem cyklicznym monitor ProducentKonsument { int Licznik=0,in=0,out=0; Condition Pelny; Condition Pusty; int Bufor[N]; void Wstaw(int x) { if (Licznik==n) Pełny.wait(); Bufor[in]=x; in=(in+1)%n; Licznik++; Pusty.signal(); } int Pobierz() { if (Licznik==0) Pusty.wait(); int x=bufor[out]; out=(out+1)%n; Licznik=Licznik-1; Pelny.signal(); return x; } }; Rozwiązanie dla wielu konsumentów i wielu producentów. Przyjmujemy, że w buforze są przechowywane liczby całkowite (int). Producent chcąc wstawić element do bufora wywołuje procedurę monitora Wstaw. Konsument chcąc pobrać element z bufora wywołuje Pobierz. Gdy bufor jest pusty, to konsumenci są wstrzymywani na zmiennej warunkowej Pusty. Gdy bufor jest pełny to producenci są wstrzymywani na zmiennej warunkowej Pełny.

Tworzenie wątku w Javie Dwie metody: class Worker extends Thread { } public void run() { } System.out.println("Wątek roboczy"); public class First { } public static void main(string args[]) { } Worker runner = new Worker(); runner.start(); System.out.println("Wątek główny"); runner.join(); Rozszerzenie klasy Thread Implementacja interfejsu Runnable Rozszerzenie klasy Thread Metoda run jest wykonywana w odrębnym wątku Deklarujemy obiekt klasy Metoda start() uruchamia wątek. Reprezentowany przez obiekt klasy Worker. Metoda join() zawiesza aktualny wątek do momentu zakończenia wątku reprezentowanego przez obiekt klasy Thread.

Metody synchronizowane w Javie Z każdym obiektem w Javie związany jest zamek (ang. lock). Metoda jest zsynchronizowana, jeżeli przed jej deklaracją stoi słowo kluczowe synchronized. Zamek gwarantuje wzajemne wykluczanie metod synchronizowanych obiektu Aby wykonać metodę synchronizowaną wątek musi wejść w posiadanie zamka. Wątek kończąc metodę synchronizowaną zwalnia zamek Jeżeli wątek próbuje wywołać metodę synchronizowaną, a zamek jest już posiadany przez inny wątek (wykonujący właśnie metodę synchronizowaną), to jest zostaje on zablokowany i dodany kolejki wątków oczekujących na zwolnienie zamka.

Blokada (ang. lock) obiektu w Javie

Producent-konsument w Javie z semi-aktywnym oczekiwaniem class ProducentKonsument { int Licznik=0,in=0,out=0; static final int N=100; int Bufor[N]; public void Wstaw(int x) { while (Licznik==N) Thread.yield(); synchronized(this) { Bufor[in]=x; in=(in+1)%n; Licznik++; } } public int Pobierz() { while(licznik==n) Thread.yield(); synchronized(this) { int x=bufor[out]; out=(out+1)%n; Licznik=Licznik-1; } return x; } }; Thread.yield pozwala na przekazanie sterowania innemu wątkowi lub procesowi. Ciągle jest to aktywne czekanie - nie zalecane. Synchronizowany blok kodu Często nie ma konieczności synchronizowania całej metody W danej jeden wątek może wykonywać synchronizowany blok kodu jednego obiektu. Wątek ten posiada blokadę obiektu W książce Applied Operating Systems Concepts użyto synchronizowanych metod. Czy jest to poprawne? Powyższe rozwiązanie jest poprawne wyłącznie dla jednego procesu konsumenta i jednego procesu producenta. (Dlaczego?).

Metody wait oraz notify Wątek posiadający blokadę obiektu może wykonać metodę wait. (tego obiektu) Wątek natychmiast zwalnia blokadę (inne wątki mogą wejść w posiadanie blokady) Zostaje uśpiony.. Umieszczany jest w kolejce wątków zawieszonych (ang. wait set) obiektu. Należy obsłużyć wyjątek InterruptedException. Wątek posiadający blokadę obiektu może wykonać metodę notify. Metoda ta sprawia, że jeden wątek z kolejki wątków zawieszonych zostanie przesunięty do kolejki wątków oczekujących na zwolnienie blokady. Metoda notifyall powoduje przeniesienie wszystkich wątków zawieszonych. W Javie istnieją również metody suspend i resume. Są niebezpieczne i nie należy ich stosować!!!

Blokada obiektu w Javie (wersja ostateczna) Jeden wątek jest właścicielem wykonuje kod synchronizowany Entry Set - wątki oczekujące na wejście w posiadanie zamka. Wait Set wątki zawieszone poprzez metodę wait Wywołanie metody notify przenosi wątek z wait set do entry set Obiekt w Javie odpowiada monitorowi z maksymalnie jedną zmienną warunku. To rozwiązanie obniża wydajność synchronizacji - każdy wątek po obudzeniu, musi sprawdzić czy obudziło go zdarzenie na które czekał. W przypadku producenta - konsumenta z ograniczonym buforem mamy dwa typy zdarzeń (bufor niepusty oraz bufor niepełny)

Producent konsument z wykorzystaniem metod wait/notify class ProducentKonsument { int Licznik=0,in=0,out=0; int Bufor[N]; public synchronized void Wstaw(int x) { while (Licznik==N) try { wait();} catch(interruptedexception e) {;} Bufor[in]=x; in=(in+1)%n; Licznik++; notifyall(); } Słabość metod wait/notify - trzeba do skutku sprawdzać warunek. public synchronized int Pobierz() { while (Licznik==0) try { wait();} catch(interruptedexception e) {;} int x=bufor[out]; out=(out+1)%n; Licznik=Licznik-1; notifyall(); return x; } };

Synchronizacja w Javie, a monitory. W monitorze możemy zadeklarować wiele zmiennych warunkowych. Klasa w Javie w przybliżeniu odpowiada monitorowi z jedną zmienną warunkową. Różnice są widoczne w przypadku rozwiązania problemu producentkonsument. Wersja z monitorami wykorzystuje dwie zmienne warunkowe W wersji w Javie konsument oczekujący na pojawienie się elementu w buforze może zostać powiadomiony przez innego konsumenta. Z tego powodu po obudzeniu należy raz jeszcze sprawdzić warunek (pętla while). Brak zmiennych warunkowych prowadzi do niskiej wydajności: np. budzeni są wszyscy czekający konsumenci ale tylko jeden z nich może kontynuować. Specyfikacja Javy mówi, że wątek wywołujący metodę notify kontynuuje pierwszy. Odpowiada to semantyce Mesa.

Biblioteka POSIX Threads - implementacja monitora Dostarcza typy i operacje dla semaforów (sem_t) oraz mutexów (pthread_mutex_t) realizujących wzajemne wykluczanie. Dostarcza typ (pthread_cond_t) dla zmiennych warunkowych i niepodzielną operację pthread_cond_wait(condition,mutex) usypiającą wątek na zmiennej warunkowej i jednocześnie zwalniającą blokadę mutex. Po obudzeniu wątku oczekującego na zmiennej warunku (przez pthread_cond_signal albo pthread_cond_broadcast) nastąpi ponowna automatyczna re-akwizycja muteksa, przed powrotem z funkcji pthread_cond_wait. Ponadto mamy operację na zmiennych warunkowych pthread_cond_signal (obudza jeden zawieszony wątek) i pthread_cond_broadcast (obudza wszystkie zawieszone wątki). pthread_mutex_t mutex; // Realizuje wzajemne wykluczanie wew. monitora void Funkcja_Monitora() { pthread_mutex_lock(&mutex); // Wejście do monitora... if ( ) { pthread_mutex_unlock(&mutex); return; // Teraz też opuszczamy monitor - pamiętać o return!!! }... pthread_mutex_unlock(&mutex); // Opuszczenie monitora }

POSIX Threads - zmienne warunkowe monitora Każda zmienna warunkowa deklarowana jest jako zmienna typu pthread_condition_t i inicjowana przy pomocy pthread_cond_init: pthread_cond_t condition; pthread_cond_init(&condition,null); Jeżeli wątek przebywający wewnątrz funkcji monitora (a zatem posiadający mutex), zechce wykonać operację wait na zmiennej warunku, może wykonać następujący kod: // Zwolnienie muteksa i oczekiwanie na zmiennej warunku. pthread_cond_wait(&condition,&mutex); // Obudzenie nastąpi po wykonaniu operacji signal lub broadcast // i re-akwizycja muteksa. Wątek chcący wykonać operację signal monitora (i przebywający wewnątrz monitora tzn. posiadający muteks) wykonuje następujący kod: // Czy to jest semantyka Hoare'a czy też Mesa? pthread_cond_signal(&condition);

Planowanie (szeregowanie) procesów (ang. process scheduling)

Rodzaje planowania Planowanie długoterminowe. Decyzja o dodaniu procesu do puli procesów wykonywanych (systemy wsadowe). Określa stopień wieloprogramowości. Planowanie średnioterminowe. Decyzja o dodaniu (usunięciu) procesu do puli procesów częściowo lub całkowicie obecnych w pamięci. Związane z wymianą i zarządzaniem pamięcią. Planowanie krótkoterminowe. Decyzja o przyznaniu procesowi (w stanie Gotowy) procesora. (dzisiejszy wykład) Planowanie dysku. Decyzja o wyborze żądania we-wy spośród żądań zgłoszonych przez procesy. W interakcyjnych systemach z podziałem czasu planowanie długoterminowe (często również planowanie średnioterminowe) może nie występować. Przykład: Linux.

Kryteria planowania Systemy wsadowe Stopień wykorzystania procesora Przepustowość (ang. throughput) liczba procesów wykonanych w ciągu jednostki czasu. Systemy interakcyjne. Czas reakcji na zdarzenie (ang. response time) Systemy czasu rzeczywistego Zaspokojenie terminów. (Niespełniony termin == awaria systemu) Przewidywalność.

Poziomy planowania a stany procesu

Planowanie a kolejki procesów w systemie (Stallings) Kolejki procesów oczekujących na procesor (ang. Ready), uśpionych (ang. Blocked) i zawieszonych (ang. suspended).

Dwa typy zachowań procesów Proces zorientowany na obliczenia Całkowite wykorzystanie CPU Fazy procesora Fazy we-wy Proces zorientowany na we-wy Całkowite wykorzystanie CPU Faza procesora Faza we-wy

Planowanie z wywłaszczaniem (ang. preemption) oraz bez wywłaszczania Planowanie możemy wykonywać gdy proces: 1. Przeszedł do stanu aktywnego do stanu oczekiwania (uśpienia) n.p. z powodu zgłoszenia zamówienia we-wy. 2. Proces przeszedł ze stanu aktywnego do stanu gotowego n.p. z powodu przerwania 3. Proces przeszedł od stanu oczekiwania do stanu gotowego. 4. Proces zakończył pracę. Jeżeli planowania dokonujemy wyłącznie w sytuacjach 1. oraz 4. to mówimy o planowaniu bez wywłaszczania. Procesowi nigdy nie zostanie odebrany procesor, chyba że proces sam zrzeknie się procesora. Jeżeli planowania dokonujemy dodatkowo w sytuacjach 2. i 3. to mówimy o planowaniu z wywłaszczaniem.

First Come First Served (FCFS) Przyjmijmy, że procesy nadchodzą w kolejności P 1, P 2, P 3 Diagram Gannt'a: P 1 P 2 P 3 0 24 27 30 Średni czas oczekiwania: (0+24+27)/3=17. Przyjmijmy, że procesy nadchodzą w kolejności P 2, P 3, P 1 Diagram Gannt'a: P 2 P 3 P 1 0 3 6 30 Średni czas oczekiwania: (0+3+6)/3=3

First Come First Served (FCFS) Proces, który pierwszy został dodany do kolejki procesów gotowych, jest wykonywany jako pierwszy. Procesy otrzymują procesor na zasadzie FIFO. Nie ma wywłaszczania Przykład: Proces Czas procesora P 1 24 P 2 3 P 3 3 Problem: Proces o dużym zapotrzebowaniu na procesor opóźnia wszystkie procesy czekające za nim w kolejce.

Shortet Job First (SJF) Proces o najkrótszej kolejnej fazie procesora wykonują się jako pierwsze Dwie wersje: SJF bez wywłaszczania. SJF z wywłaszczaniem (zwany także Shortest Remaining Time First, w skrócie SRTF). SJF jest optymalny. Minimalizuje średni czas oczekiwania.

Przykład: SJF bez wywłaszczania Proces Czas nadejścia Czas cyklu CPU P 1 0.0 7 P 2 2.0 4 P 3 4.0 1 P 4 5.0 4 Diagram Gannt'a P 1 P 3 P 2 P 4 0 3 7 8 12 16 Średni czas oczekiwania: (0+6+3+7)/4=4

Przykład: SJF z wywłaszczaniem Proces Czas nadejścia Czas cyklu CPU P 1 0.0 7 P 2 2.0 4 P 3 4.0 1 P 4 5.0 4 Diagram Gannt'a P 1 P 2 P 3 P 2 P 4 P 1 0 2 4 5 7 11 16

Prognozowanie długości cyklu procesora (uśrednianie wykładnicze) t n τ n α Aktualny czas n-tego cyklu. Prognozowany czas n-tego cyklu. Stała z przedziału [0,1]. τ n+1 =αt n +(1 α) τ n

Planowanie z wykorzystaniem priorytetów Każdy proces otrzymuje liczbę zwaną piorytetem. Proces o najwyższym priorytecie otrzymuje procesor. Często najwyższy priorytet = najmniejsza liczba. SJF jest przykładem planowania z wykorzystaniem priorytetów. W tym przypadku priorytetem jest długość fazy procesora. Zagłodzenie: Procesy o niewielkim priorytecie mogą oczekiwać w nieskończoność. W MIT w 1973 przy złomowaniu komputera wykryto niskopriorytetowy proces zgłoszony do wykonania w 1967. Postarzanie (ang aging): Zwiększaj priorytet procesu długo oczekującego na procesor.

Planowanie rotacyjne (ang. round robin) Algorytm wykorzystuje wywłaszczanie przy pomocy przerwania zegara. Proces otrzymuje kwant czasu procesora. Jeżeli po upływie kwantu czasu proces nie zakończy cyklu procesora. Proces jest wywłaszczany i dodawany na koniec kolejki procesów gotowych. Kolejny proces z kolejki procesów gotowych otrzymuje kwant czasu. Algorytm stosowany powszechnie w systemach z podziałem czasu. Jak dobierać długość kwantu czasu. Typowa wartość: 10ms. Bardzo duży kwant czasu => algorytm degeneruje się do FCFS, duży średni czas oczekiwania Bardzo mały kwant czasu => straty wydajności związane z przełączeniam kontekstu.

Przykład planowania rotacyjnego Zakładamy kwant czasu 20ms. Process Czas cyklu procesora P 1 53 P 2 17 P 3 68 P 4 24 Diagram Gannt'a P 1 P 2 P 3 P 4 P 1 P 3 P 4 P 1 P 3 P 3 0 20 37 57 77 97 117 121 134 154 162

Wpływ długości kwantu czasu na liczbę przełączeń kontekstu

Przykładowe zadanie na egzamin Proces P 1 : 10ms CPU, 20 ms I/O, 20 ms CPU, 10 ms I/O Proces P 2 : 40 ms I/O, 20 ms CPU. Proces P 3 : 50 ms CPU, 10 ms I/O, 20 ms CPU. Narysuj diagramy Gannt'a obrazujące planowanie procesora przy pomocy algorytmów: FCFS, SJF, SJF z wywłaszczaniem, i rotacyjnego z kwantem czasu 10ms.

Planowanie z wykorzystaniem kolejek wielopoziomowych (ang. multilevel quee) Kolejka procesów gotowych jest podzielona na kilka kolejek, na przykład Kolejka procesów pierwszoplanowych (interakcyjnych) Kolejka procesów drugoplanowych Każda kolejka ma swój własny algorytm planowania, na przykład Kolejka procesów pierwszoplanowych, alg. Rotacyjny Kolejka procesów drugoplanowych, alg. FCFS Możliwości podziału czasu procesora pomiędzy kolejki. Procesy pierwszoplanowe wykonują się zawsze pierwsze. Time Slice - każda kolejka ma przydzielony pewien stopień wykorzystania procesora Procesy pierwszoplanowe 80% Procesy drugoplanowe 20%

Kolejki wielopoziomwe

Wielopoziomowe kolejki ze sprzężeniem zwrotnym (ang. multilevel feedback queue) Proces może być przemieszczany pomiędzy kolejkami. Jeżeli proces zużywa za dużo czasu procesora zostaje przemieszczony do kolejki o niższym priorytecie Proces oczekujący bardzo długo może zostać przemieszczony do kolejki o wyższym priorytecie. Zapobiega to zagłodzeniu. Generalnie musimy podać. Liczbę kolejek. Algorytm planowania dla każdej kolejki. Metoda użyta do awansowania procesu do kolejki o wyższym priorytecie Metoda użyta do degradowania procesu do kolejki o niższym priorytecie.

Wielopoziomowe kolejki ze sprzężeniem zwrotnym Trzy kolejki o malejącym priorytecie. Kolejka 1: planowanie rotacyjne z kwantem 8ms. Kolejka 2: planowanie rotacyjne z kwantem 16 Kolejka 3: FCFS

Szeregowanie wątków zakres rywalizacji Process Contention Scope (PTHREAD_SCOPE_PROCESS) wątki jednego procesu o tym atrybucie grupowane są razem i grupa rywalizuje o procesor. System Contention Scope (PTHREAD_SCOPE_SYSTEM) wątek współzawodniczy o procesor także z wątkami innych procesów. Zmieniane funkcją pthread_attr_setscope Przykład 1 Przykład 2 Proces P1 10 wątków PTHREAD_SCOPE_PROCESS Proces P2 jeden wątek (nieważne jaki) Każdy z wątków otrzyma 1/11 czasu procesora (zakładając równe priorytety). Jeden proces a w nim 4 wątki PTHREAD_SCOPE_PROCESS i 4 wątki PTHREAD_SCOPE_SYSTEM Każdy z wątków PTHREAD_SCOPE_SYSTEM otrzyma 1/5 czasu procesora, wszystkie wątki PTHREAD_SCOPE_PROCESS otrzymują razem 1/5 czasu procesora