Systop5

background image

5. ZAGADNIENIA KOORDYNACJI PROCESÓW

Przez koordynację procesów będziemy rozumieli ich synchronizację (czyli

uwzględnianie

wzajemnych uzależnień czasowych) oraz komunikację (czyli wzajemne

przekazywanie sobie

informacji).

Potrzeba synchronizacji jest na ogół związana z komunikacją - informacja nie może

zostać odczytana

przez jeden proces, jeśli nie została wcześniej zapisana przez inny proces.

Komunikacja pomiędzy

procesami może odbywać się przez kanał komunikacyjny (na przykład

realizowany jako bufor

współdzielonego pliku) lub przez pamięć dzieloną (segment pamięci fizycznej

udostępniony przez

system operacyjny obydwóm procesom).

Zapisy / odczyty do / z pamięci dzielonej mogą być dokonywane przez oba procesy

bez żadnych

ograniczeń, więc muszą być synchronizowane przez dodatkowe mechanizmy

systemowe.

Kanał komunikacyjny służy zazwyczaj tylko do komunikacji jednokierunkowej, a

mechanizmy

synchronizacji są zawarte w samych procedurach zapisu / odczytu do / z kanału.

background image

Jeśli jeden proces tylko produkuje dane i umieszcza je w pewnym medium
komunikacyjnym, a drugi

proces tylko je stamtąd pobiera (w takiej samej kolejności, w jakiej zostały
włożone), mówimy, że te

procesy tworzą układ producent - konsument

(producer - consumer)

. Jeśli

medium komunikacyjne

posiada pewną pojemność (bufor), działania producenta są w pewnym stopniu
niezależne od działań

konsumenta - może on produkować dane „na zapas” i umieszczać je w buforze.
Konsument natomiast

nie może „konsumować” danych szybciej, niż dostarcza je producent.

Jeśli pojemność bufora jest nieskończenie duża, proces producenta jest
całkowicie niezależny od

procesu konsumenta. Jeśli pojemność bufora jest zerowa, musi zachodzić pełna
synchronizacja

pomiędzy produkcją i konsumpcją (włożenie i pobranie każdej danej musi
nastąpić w tej samej

chwili). Najbardziej typowym przypadkiem jest skończona, niezerowa pojemność
bufora. Bufor

skończony jest często realizowany jako tablica cykliczna.

produce
nt

konsument

bufor

background image

Niektóre procesy systemowe pełnią rolę serwerów, czyli dostarczycieli
określonych usług (na

przykład obliczają jakąś funkcję, podają czas, wysyłają pliki pod podany adres
itp.). Serwery

przeważnie przebywają w stanie zawieszenia, oczekując na zgłoszenia procesów
potrzebujących

usługi, czyli klientów. Po zgłoszeniu zapotrzebowania klient zostaje przeważnie
zawieszony i czeka

na dostarczenie odpowiedzi przez serwer. Potem pobiera odpowiedź, a serwer
zostaje ponownie

zawieszony.

serwer

aktywność

klient
zawieszenie

t

Uwaga

1) Okresowo mogą występować spiętrzenia zgłoszeń klientów przysyłanych
szybciej, niż serwer

jest w stanie je obsłużyć. W takim przypadku są one wpisywane do kolejki
zgłoszeń
serwera

i wybierane stamtąd w takiej samej kolejności, jak przybyły.

2) Serwery dzielą się na iteracyjne i współbieżne. Serwery iteracyjne same
(jako jeden proces)

zajmują się pobieraniem zgłoszeń z kolejki, obsługą i odesłaniem odpowiedzi.
Serwery współ-

bieżne pobierają tylko zgłoszenia, a następnie tworzą procesy potomne i im
zlecają całą dalszą

obsługę. Obecnie większość serwerów jest realizowanych jako współbieżne.

background image

Przekazywanie informacji pomiędzy procesami powinno odbywać się w
określonych porcjach.

Byłoby niekorzystne, gdyby proces odczytujący pobrał tylko fragment
komunikatu przekazywanego

przez inny proces. Podobnie w przypadku aktualizacji zapisu pewnego rekordu w
bazie danych przez

jeden proces, inne procesy nie powinny w tym samym czasie próbować odczytać
tego rekordu, gdyż

mogłyby odczytać częściowo nową, a częściowo starą zawartość - otrzymałyby
wtedy niespójne dane.

Fragment kodu procesu, który wykonuje operację na współdzielonych danych
taką, że w tym samym

czasie żaden inny proces nie powinien operować na tych danych, nazywamy
sekcją krytyczną

(critical section)

tego procesu. Założenie, że w dowolnej chwili co najwyżej jeden

proces (spośród

grupy współpracujących procesów) może wykonywać swoją sekcję krytyczną,
nazywamy zasadą

wzajemnego wykluczania

(mutual exclusion)

.

Aby wzajemne wykluczanie mogło być zrealizowane, każde wykonanie sekcji
krytycznej musi być

poprzedzone wykonaniem sekcji wejściowej, w której proces zgłasza potrzebę
wejścia do sekcji

krytycznej (i czeka na zgodę), a po wykonaniu sekcji krytycznej musi nastąpić
wykonanie sekcji

wyjściowej, w której proces informuje inne o opuszczeniu sekcji krytycznej.
Pozostałą część kodu

procesu (nie należącą do żadnej z powyższych sekcji) nazywamy resztą.

background image

Przyjęcie założenia o istnieniu sekcji krytycznych pociąga za sobą konieczność
spełnienia

następujących warunków:

1) wzajemne wykluczanie;

2) postęp

(progress)

- jeżeli w pewnym momencie żaden proces nie wykonuje

swojej sekcji

krytycznej, a pewna liczba procesów kandyduje do tego (weszła do swoich
sekcji wejściowych),

to wybór jednego z nich musi nastąpić w ograniczonym czasie;

3) jeżeli którykolwiek proces wszedł do swojej sekcji wejściowej, to przed
otrzymaniem przez niego

zgody na wejście do sekcji krytycznej tylko ograniczona liczba innych
procesów może taką zgodę

otrzymać.

Powyższe warunki związane są z unikaniem pewnych niekorzystnych zjawisk,
które mogą wystąpić

w przypadku niewłaściwej koordynacji procesów. Te zjawiska to blokada

(deadlock)

oraz głodzenie

(starvation)

procesów.

background image

Blokada zachodzi, gdy pewna liczba procesów jest w stanie oczekiwania na
zdarzenie, które może być

wynikiem jedynie wykonywania (postępu) jednego z nich.

Przykład

Przepis prawny obowiązujący w stanie Kanzas na przełomie XIX i XX wieku:
„Jeżeli dwa pociągi

zbliżają się do siebie jadąc po krzyżujących się torach, to każdy z nich powinien
zatrzymać się i nie

ruszać, dopóki ten drugi nie odjedzie”.

Przykład

Jeśli cztery pojazdy dojadą jednocześnie z różnych stron do skrzyżowania dróg
równorzędnych,

każdy z nich powinien udzielić pierwszeństwa pojazdowi po jego prawej stronie.

Przykład

Problem ucztujących filozofów (por. następny slajd).

background image

Problem ucztujących filozofów.

n =

5

Założenia:

a) każdy filozof może przebywać w dwóch stanach:

myślenie

i

jedzenie

;

b) każdy widelec jest zasobem współdzielonym przez dwóch sąsiadów na
zasadzie wyłączności

dostępu;

c) do jedzenia potrzebne są dwa widelce;

d) widelce muszą być brane sekwencyjnie (nie jednocześnie) przez jednego
filozofa;

e) czasy przebywania w stanach

myślenie

i

jedzenie

są zawsze skończone.

Jeżeli wszyscy filozofowie naraz podniosą np. lewe widelce, dojdzie do blokady.

F
0

F1

F2

F3

F4

w1

w2

w3

w4

w0

background image

Warunki konieczne i dostateczne powstawania blokad:

1) wzajemne wykluczanie (niepodzielność pewnego zasobu - np. procesora lub
wybranej lokaty

w pamięci);

2) brak wywłaszczeń (system nie może „siłą” odbierać pewnych zasobów
procesom);

3) oczekiwanie cykliczne (musi istnieć zbiór procesów { P

1

, P

2

, ... , P

n

}, gdzie n

> 1, taki, że P

1

czeka

na zasób przetrzymywany przez P

2

, ... , P

n

czeka na zasób przetrzymywany

przez P

1

).

Wynika z tego, że każdy z procesów biorących udział w blokadzie musi mieć
przydzielony pewien

zasób i dodatkowo czekać jeszcze na przydzielenie pewnego innego zasobu.

Głodzenie zachodzi, gdy co najmniej jeden proces oczekuje w nieskończoność na
przydzielenie

pewnego zasobu (choć teoretycznie mógłby go otrzymać), podczas gdy inne
procesy wymieniają się

tym zasobem.

Przykład

Obsługa na zasadzie stosu

(last in, first servised)

.

background image

Do zjawiska głodzenia procesów może doprowadzić niewłaściwie zorganizowany
system priorytetów

procesów (współczynników liczbowych przyporządkowanych procesom, które
wskazują, na ile

wykonywanie danego procesu jest pilne z punktu widzenia systemu
operacyjnego). Jeśli przez cały

czas pojawiają się w systemie procesy o wysokich priorytetach, może się zdarzyć,
że pewien proces

o niskim priorytecie nigdy nie będzie wybrany do wykonania. Metodą
zapobiegania takiemu zjawisku

jest system priorytetów zmiennych w czasie - priorytet każdego oczekującego
na wykonanie procesu

stopniowo rośnie, aż wreszcie w którymś momencie musi on uzyskać przydział
procesora. Takie

rozwiązanie jest zastosowane na przykład w systemie Unix.

W języku matematycznej teorii zarządzania procesami własność systemu
zapewniająca brak głodzenia

procesów oczekujących na przydział zasobów nazywana jest uczciwością

(fairness)

. Wyróżniane są

różne rodzaje uczciwości w zależności od tego, czy proces zgłasza
zapotrzebowanie na zasób przez

cały czas, czy też okresowo, i od tego, czy czas oczekiwania na przydział jest
zależny od liczby innych

procesów ubiegających się o ten sam zasób.

background image

W programowaniu procesów współbieżnych istotną rolę odgrywa pojęcie
niepodzielności operacji.

W przypadku wykonywania sekcji krytycznej chcemy mieć zapewnione, że
proces będzie mógł ją

wykonać jako całość, bez przerywania wykonania przez inne procesy. Jeśli
operacja na zasobach

jest wykonywana przez pojedyncze wywołanie funkcji systemowej, to
niepodzielność jej wykonania

jest gwarantowana przez system operacyjny (procesy wykonywane w trybie
jądra systemu nie

podlegają wywłaszczaniu). Przykładami takich operacji są pojedyncze zapisy i
odczyty do / z

kanału komunikacyjnego (na przykład kolejki komunikatów).

Gwarancji niepodzielności nie ma natomiast w przypadku operowania na
pamięci wspólnej.

Pojedyncze instrukcje w językach wyższego poziomu, takie jak na przykład x =
y + z ; w języku C,

gdzie x, y, z są nazwami zmiennych, którym zostały przyporządkowane lokaty
w segmencie

pamięci wspólnej, są tłumaczone na cały ciąg rozkazów maszynowych, którego
wykonywanie może

być przeplatane z wykonaniami innych procesów (być może również
operujących na tych

zmiennych) w dowolny sposób.

background image

Jeżeli mają być wykonywane operacje na pamięci wspólnej, lub bardziej złożone
operacje na

dowolnych zasobach, programista musi sam zadbać o ich zabezpieczenie.
Najprostszym mecha-

nizmem abstrakcyjnym służącym do tego celu są semafory. Podstawowym
rodzajem semafora

jest semafor binarny, będący obiektem, którego jedyne pole może przyjmować
tylko wartości

0 i 1, a jedyne operacje, jakie można na nim wykonać, to:

P (czekaj)

V (sygnalizuj)

Definicje tych operacji jest następująca:

P(S) - jeżeli S>0, to zmniejsz S o 1, w przeciwnym razie wstrzymaj
wykonywanie procesu;

V(S) - jeżeli są jakieś procesy wstrzymane przez semafor S, to wznów jeden z
nich, w przeciwnym

razie jeśli S=0, to zwiększ S o 1.

background image

Uwaga.

1) Skutek próby otwarcia otwartego semafora binarnego zależy od
implementacji. Dojście do

takiej sytuacji świadczy o błędzie w programie (i system operacyjny
zazwyczaj reaguje

sygnalizacją błędu).

2) Same operacje na semaforach muszą być wykonywane niepodzielnie. W
systemach z przeplo-

tem realizowane są jako funkcje systemowe, natomiast w sytuacji
rzeczywistej równoległości

ich implementacja musi być wspierana przez odpowiedni sprzęt (procesory
z niepodzielnymi

rozkazami typu test-and-set).

3) Niedeterminizm uruchamiania procesów czekających pod semaforem może
podlegać różnym

ograniczeniom. Wyróżniamy na przykład semafor ze zbiorem
oczekujących
, gdzie wybór

procesu spośród oczekujących pod semaforem jest zupełnie przypadkowy, i
semafor z kolejką

oczekujących, gdzie procesy są uwalniane spod semafora w takiej samej
kolejności, w jakiej

do niego przybyły.

background image

Jednym z klasycznych problemów programowania współbieżnego jest tak
problem czytelników

i pisarzy. Problem ten jest abstrakcją problemu dostępu do wielodostępnej bazy
danych. Każdy

proces aktualizujący dane (pisarz) musi mieć wyłączność dostępu do danych
(czytelni), ale procesy,

które tylko czytają (czytelnicy), mogą pracować jednocześnie.

Problem ten jest możliwy, ale trudny do rozwiązania przy użyciu tak
elementarnych narzędzi, jak

semafory. Typowe rozwiązania opierają się na wysokopoziomowych
mechanizmach synchronizacji,

takich jak na przykład monitory lub regiony (dostępnych w językach wysokiego
poziomu przeznaczo-

nych do programowania współbieżnego - na przykład w języku ADA).

background image

Idea rozwiązania:

(gwarantującego niemożliwość głodzenia zarówno

czytelników, jak i pisarzy)

Przybywający pisarze ustawiani są w kolejkę prostą, przybywający czytelnicy
gromadzeni są

w zbiorze. Mechanizm koordynujący wpuszcza na przemian pojedynczych
pisarzy i cały zbiór

zgromadzonych czytelników. Wpuszczenie może mieć miejsce dopiero po
opuszczeniu czytelni

przez poprzednich użytkowników / użytkownika. Jeżeli kolejka oczekujących
pisarzy jest pusta,

a w czytelni przebywają czytelnicy, każdy nowo przybyły czytelnik jest od razu
wpuszczany.

Jeśli w zbiorze nie oczekują żadni czytelnicy, kolejno przybywający pisarze są
wpuszczani po

zakończeniu pobytu w czytelni przez poprzednika.

kolejka pisarzy

zbiór
czytelników

czytelnia


Document Outline


Wyszukiwarka

Podobne podstrony:
Systop2
Systop11
Systop14
Systop13
Systop8
Systop3
Systop10
Systop1
Systop9
Systop7
Systop2
Systop12
SystOper

więcej podobnych podstron