Professional Documents
Culture Documents
Operációs Rendszerek 9.B: Versenyhelyzetek
Operációs Rendszerek 9.B: Versenyhelyzetek
Operációs Rendszerek 9.B: Versenyhelyzetek
B
Versenyhelyzetek
Konkurens folyamatok közötti kommunikáció: (saját szavakkal ezt tudom elképzelni)
Olyan esetben beszélünk konkurens folyamatokról, amikor ezen folyamatok megosztott
memóriáért versenyeznek. Mivel fontos a megfelelő sorrend betartása (ha egy folyamat egy
másik által előállított eredményre vár, akkor csak azután próbálja meg kiolvasni, mikor a
másik már előállította), ezért elkerülhetetlen a kommunikáció. Ez megvalósítható egy
változón keresztül, de üzenet küldésével is.
Versenyhelyzet:
Ha kettő vagy több processzus olvas vagy ír megosztott adatokat és a végeredmény attól
függ ki és mikor fut. Versenyhelyzetre a megoldás a megelőzés kölcsönös kizárással: egy
módszer, amely biztosítja hogy ha egy processzus használ valamely megosztott változót,
fájlt, akkor a többi processzus „tartózkodjon” ettől a tevékenységtől.
Szigorú alternáció:
Process0 Process1
while(TRUE) { while(TRUE) {
while(turn != 0); while(turn != 1);
critical(); critical();
turn = 1; turn = 0;
non_critical(); non_critical();
} }
P0 belép a kritikus szekcióba, végrehajtja – eközben a P1 tevékenyen várakozik -, majd
átállítja a turn-t 1-re, így a P1 is beléphet a kritikus szekcióba, végrehajtja, majd a turn-t 0-ra
állítja. Ezen a pontot mindkét folyamat a nem kritikus szekciójában van.
Azonban ha P1 befejezi és újrakezdi a ciklust, akkor be tud lépni a CS-be. Gyorsan
végrehajtja, turn-t 1-re állítja, majd a nem kritikus szekciót is egyből befejezi, a ciklus
újraindul, viszont nem tud belépni a CS-be, mert még a másik folyamatra vár – ami még
mindig a nem kritikus szekcióban van, hogy az belépjen a CS-be.
Tehát ha a két folyamat nem egyforma sebességgel halad, akkor előfordulhat, hogy feltartják
egymást, ami viszont megszegi a 3. feltételt. Emellett a folyamatok a tevékeny várakozás
közben foglalják a CPU idejét, továbbá 2 folyamatnál többre alkalmazva még rosszabb lenne
a helyzet.
Peterson módszere:
#define FALSE 0
#define TRUE 1
#define N 2 // folyamatok száma
int turn; // melyik processzus fut
int interested[N]; // ki érdekelt
Prioritás inverzió:
Van két processz, processz H magas prioritással, processz L alacsonnyal. Az ütemezési
szabályok szerint a magasabb prioritású processz mindig megkapja a CPU-t, ha igényli. Egy
bizonyos pillanatban legyen L a kritikus szakaszban, ezalatt H váljon futásra kész állapotúvá, a
busy waiting ciklusa előtt kevéssel. Megkapva H a CPU-t, tesztel és vár ciklusban, és mivel
magasabb a prioritása, nem engedi szóhoz jutni L-t, hogy az kijusson a kritikus szakaszából,
felszabadítva H-t a várakozó ciklusból. Nyilvánvaló holtponthelyzet alakult ki.
Gyártó-fogyasztó probléma:
Szemaforok:
Egész változókban számoljuk az ébresztések számát (semaphore). Elemi műveletekből áll,
mely garantálja a versenyhelyzetek elkerülését, ha egy szemafor művelet elkezdődik, más
process nem tudja elérni a szemafort addig, míg be nem fejeződik vagy nem blokkolódik.
Két művelet:
down (sleep megfelelője): megvizsgálja hogy a szemafor értéke nagyobb-e mint
nulla?
o Ha igen, akkor csökkenti az értéket és folytatja
o Ha nulla, akkor a processzust elaltatja mielőtt befejeződne
up (wakeup megfelelője): a szemafor értékét növeli
o Ha egy vagy több processzus aludna ezen a szemaforon, akkor egyet kiválaszt
és megengedi hogy a down befejeződjön. Így olyan szemaforon végrehajtva az
up-ot, amelyen a processzek aludtak, a szemafor még mindig 0 lesz, de eggyel
kevesebb processz fog aludni.
Az olyan szemaforokat, amelynek kezdőértéke 1, és arra szolgálnak, hogy biztosítsák, hogy
kettő vagy több folyamat közül egyidőben csak egy léphessen kritikus szekciójába, bináris
szemafornak nevezzük. Ha minden folyamat pontosan azelőtt hajt végre egy down-t, mielőtt
belép a kritikus szekciójába, és pontosan utána egy up-ot, miután kilép, akkor a kölcsönös
kizárás is biztosítva van.
Fontos, hogy azonos számú down és up kell, emellett megfelelő sorrendben kell kiadni őket.
typedef int semaphore;
semaphore mutex=1, empty=N, full=0;
Monitor:
A monitor magasabb szintű szinkronizációs mechanizmus: eljárások, változók, adatstruktúrák
speciális formájú gyűjteménye. A proceszek hívhatják a monitor eljárásait, de nem férnek a
monitor belső adatstruktúráihoz (information hiding), továbbá biztosított az is, hogy egy
időben csak egy processz használhat egy monitort. A fordító biztosítja a monitorba való
belépés-re a kölcsönös kizárást (szokásosan egy bináris szemaforral), és így a programozónak
ezzel már nem kell foglakoznia. Ha egy procesz hív egy monitorban lévő eljárást, az első
néhány instrukció ellenőrzi, vajon más processz pillanatnyilag aktív-e a monitorban. Ha igen,
a hívó blokkolódik, míg a másik elhagyja a monitort.
void producer(void) void consumer(void)
{ {
while(true) while(true)
{ {
i = produce_item(); i = remove();
enter(i); consume_item(i);
} }
} }
5 filozófus problémája:
Öt filozófus ül egy asztal körül, mindegyiknek van egy soha ki nem ürülő tányér spagettije és
mindegyik tányér mellett - valójában a tányérok között - van egy villa. Spagettit enni viszont
csak két villával lehet. A filozófusok felváltva esznek vagy gondolkodnak.
Amikor egy filozófus megéhezik, megpróbálja megszerezni a tányérja melletti bal és jobb
villát, bármilyen sorrendben, de egymás után. Ha sikerül két villát szereznie, akkor eszik egy
darabig, majd leteszi a villákat és folytatja a gondolkodást.
Tételezzük fel, hogy mind az öt filozófus egyszerre felveszi a baloldali villáját, ezután egyik
sem vehet jobboldali villát: bekövetkezik a holtpont helyzet.
Úgy módosítunk a programon, hogy miután felvette a filozófus a bal villát, ellenőrzi, vajon
elérhető-e a jobb villa, s ha nem leteszi a bal villát. Ebben az esetben előfordulhat, hogy
minden filozófus egyszerre hajtja végre az algoritmust, tehát folyamatosan fel és leteszik a
villát. Az ilyen helyzetet, amelyben minden processz korlátlan ideig folytatja a futást, de
érdemben nem halad előre, éhezésnek nevezzük.
Egy lehetséges megoldás lehet, hogy a villa felvételétől a villa letételéig megvédjük a
folyamatot egy bináris mutex-szel. Elméletileg megfelelő, azonban a gyakorlatban csak 1
filozófus ehet, pedig 5 villa esetén 2 is ehetne egyszerre.
A megoldás az, hogy nyilvántartja a filozófusok állapotát. Egy filozófus csak akkor ehet, ha
egyik szomszédja sem eszik. Holtpontmentes, tetszőleges számú filozófusra esetén is
megengedi a maximális párhuzamosságot.
#include "prototypes.h";
#define N 5 // number of philosophers
#define LEFT (i-1)%N // number of i's left neighbor
#define RIGHT (i+1)%N // number of i's right neighbor
#define THINKING 0
#define HUNGRY 1
#define EATING 2
void philosopher(int i) {
while (TRUE) {
think();
take_forks(i); // acquire two forks or block
eat();
put_forks(i); // put both forks back on table
}
}
void take_forks(int i) {
down(&mutex); // enter critical region
state[i] = HUNGRY;
test(i); // try to acquire 2 forks
up(&mutex); // exit critical region
down(&s[i]); // block if forks were not acquired
}
void put_forks(int i) {
down(&mutex);
state[i] = THINKING;
test(LEFT); // see if left neighbor can now eat
test(RIGHT); // see if right neighbor can now eat
up(&mutex); // exit critical region
}
void test(int i) {
if (state[i]==HUNGRY &&
state[LEFT]!=EATING &&
state[RIGHT]!=EATING) {
state[i] = EATING;
up(&s(i));
}
}
Író-olvasó probléma:
A probléma esetén elfogadható, hogy több processz egyidejűleg olvasson az adatbázisból, de
ha egy process aktualizálja (írja) az adatbázist, akkor azt más processznek nem szabad
elérnie, még az olvasóknak sem.
Ebben a megoldásban az első olvasó, aki hozzáfér az adatbázishoz végrehajt egy down-t a
szemaforon, a következő olvasók csupán egy számlálót növelnek. Ha egy olvasó kilép, akkor
csökkenti a számlálót, az utolsó kilépő egy up-ot hajt végre a szemaforon, lehetővé téve a
blokkolt írónak, hogy belépjen.
Azonban ha állandóan érkeznek új olvasók (átfedés van köztük), akkor az író process örökké
blokkolva lesz. Erre a megoldás az, hogy ha egy író már vár, akkor az olvasókat nem engedjük
be, hanem felfüggesztődnek az író mögött, így az írónak csak azt kell megvárni, hogy az
előtte érkezett olvasók végezzenek.
typedef int semaphore;
semaphore mutex = 1;
semaphore db = 1;
semaphore rc = 0;
void reader(void)
{
while(true)
{
down(&mutex);
rc = rc + 1;
if (rc == 1)
down(&db);
up(&mutex);
read_data_base();
down(&mutex);
rc = rc – 1;
if (rc == 0)
up(&db);
up(&mutex);
use_data_read();
}
}
void writer(void)
{
while(true)
{
think_up_data();
down(&db);
write_data_base();
up(&db);
}
}
Alvó borbély probléma:
Borbélyüzlet, egy vagy több borbély és ugyanannyi borbélyszék van benne. Ezen kívül van N
szék a várakozó vendégeknek. Ha nincs vendég, a borbély alszik a borbélyszékében. Ha jön
egy vendég, felkelt egy borbélyt, az hajat vág. Ha vendég jön, de minden borbélyszék (ezzel
borbély) foglalt, akkor ha van üres várakozószék, akkor leül és várakozik. (Nem feltétlenül
érkezési sorban szolgálják majd ki.) Ha a várakozószékek is foglaltak, akkor azonnal elhagyja
a borbélyüzletet.
#include "prototypes.h"
#define CHAIRS 5
typedef int semaphore;
semaphore customers = 0;
semaphore barbers = 0;
semaphore mutex = 1;
int waiting = 0;
void Barber(void) {
while (TRUE) {
down(customers);// go to sleep if # of customers is 0
down(mutex); // acquire access to 'waiting'
waiting = waiting - 1;// decrement # of waiting customers
up(barbers); // one barber is now ready to cut hair
up(mutex); // release 'waiting'
cut_hair(); // cut hair (outside critical region)
}
}
void Customer(void) {
down(mutex); // enter critical region
if (waiting < CHAIRS) { // if there are free chairs
waiting = waiting + 1; // incr # of waiting customers
up(customers); // wake up barber if necessary
up(mutex); // release access to 'waiting'
down(barbers); // sleep if # of free barbers is 0
get_haircut(); // be seated and be serviced
} else {
up(mutex); // shop is full; do not wait
}
}