Úkolem tohoto cvičení je:
V minulém cvičení jsme se setkali s pojmem časový souběh - chybné chování, které může nastat, pokud více paralelně běžících procesů komunikuje prostřednictvím společné datové oblasti. Ta oblast programu, ve které může k časovému souběhu dojít, se nazývá kritická sekce. V našem příkladu byl kritickou sekcí příkaz ``x:=x+1''.
K časovému souběhu by nemohlo dojít, pokud bychom dokázali zajistit, aby žádné dva procesy neprováděly své kritické sekce současně. Pro správný běh procesů tedy potřebujeme zajistit vzájemné vyloučení procesů, tj. aby proces, který vstoupil do kritické sekce mohl zabránit jinému učinit totéž.
Aby bylo možné dané řešení používat prakticky, musí splňovat následující 4 podmínky:
V případě pseudoparalelního běhu je nejjednodušším řešením zakázat přerušení při vstupu do kritické sekce a povolit je při výstupu. Pokud jsou přerušení zakázána, nemůže dojít k přerušení od časovače a tím ani k přepnutí procesu.
Takové řešení se skutečně užívá, například pro časově kritické procesy uvnitř jádra operačního systému. Dát uživatelským procesům právo zakázat přerušení však není vhodné, protože uživatelské programy mohou být (úmyslně či neúmyslně) chybné - domyslete, co by se stalo, pokud by proces přerušení "zapoměl" povolit.
Existuje několik softwarových řešení problému vzájemného vyloučení, ale tato jsou poměrně složitá a vyžadují aktivní čekání (tj. proces čekající na vstup do kritické sekce konzumuje drahocenný čas CPU opakovaným testováním hodnoty některé proměnné).
V roce 1965 navrhl E. W. Dijkstra dvě primitivní operace, které podstatně zjednodušily synchronizaci procesů. Tyto operace, nazývané P a V, pracují nad tzv. semafory.
Nechť semafor s je nezáporné celé číslo. Pak můžeme operace P a V definovat takto:
Jak lze snadno ověřit, semafory splňují výše uvedené "pomínky praktické použitelnosti."
Pomocí semaforů je řešení problému kritické sekce již poměrně snadné. Vytvoříme semafor s počáteční hodnotou 1, který bude mít funkci jednoduchého zámku - pouze jeden proces bude moci provést operaci P a vstoupit do své kritické sekce.
Následující pseudokód ukazuje správné řešení úlohy, na které byl ilustrován problém časového souběhu:
var mutex: semaphore; { semafor mutex s počáteční hodnotou 1 } x: integer; proces1: begin { nějaké instrukce } P(mutex); x:=x+1; { kritická sekce, může obsahovat libovolně instrukcí } V(mutex); { nějaké instrukce } end; proces2: begin { nějaké instrukce } P(mutex); x:=x+1; { kritická sekce } V(mutex); { nějaké instrukce } end;
Existuje i jednodušší forma semaforů, nazývaných binární semafory, které mohou nabývat pouze hodnot 0 a 1.
Později uvidíme, že výše popsané obecné (čítající) semafory lze užít i pro řešení některých problémů meziprocesové komunikace, například pro řešení problému producent/konzument (The Bounded Buffer Problem, Dijkstra 1968).
Protože většina současných počítačů nemá podporu operací P a V na úrovni strojového jazyka, bývají funkce P a V implementovány operačním systémem. Na jednoprocesorových systémech může jejich atomičnost zajistit zakázáním přerušení po dobu operace, ve víceprocesorových systémech se užívá primitivních operací s uzamčením sběrnice.
Nyní si popíšeme jeden z možných způsobů implementace na jednoprocesorovém stroji.
S každým semaforem s je sdružena celočíselná proměnná s.c a seznam blokovaných procesů s.L. Semaforům dovolíme nabývat i záporných hodnot proměnné s.c, pak absolutní hodnota s.c vyjadřuje počet blokovaných procesů.
Proces, který nemůže dokončit operaci P bude zablokován a uložen do seznamu procesů blokovaných na semaforu s. Pokud při operaci V není seznam prázdný, vybere se ze seznamu jeden proces a odblokuje se.
Výše popsanou implementaci můžeme vyjádřit následujícím pseudokódem:
P(s): zakaž přerušení; s.c := s.c -1; if s.c<0 then begin zařaď volající proces do seznamu s.L; zablokuj volající proces; naplánuj další připravený proces; s povolením přerušení přepni kontext na naplánovaný proces end; povol přerušení; V(s): zakaž přerušení; s.c:=s.c+1; if s.c<=0 then begin vyber proces ze seznamu s.L; odblokuj vybraný proces end; povol přerušení;
Výše uvedený pseudokód se nezabývá implementačními detaily, například organizací datových struktur a kontrolou chyb (např. je-li při operaci V záporné s.c a přitom s.L je prázdné).
Do vámi vytvořeného modulu podpory procesů implementujte čítající semafory jako procedury s jedním argumentem.
Lukáš Petrlík