Kölcsönös kizárás - szemaforok Operációs rendszerek Kölcsönös kizárás - szemaforok
Miről lesz szó? A probléma: vetélkedés erőforrásokért Alapfogalmak, kívánalmak Megoldások Váltogatás és ki érdekelt, zároló-változó stb. (primitívek, alapalgoritmusok) szemaforok Problémák megoldásai, esettanulmányok Vadász
A sorbaállási modell Erőforrásokért versengő processzek rendszerének analizálására Várakozási sorok (queue) képezhetők (téglalapok) Vannak erőforrások (körök) Ütemezések (függőleges vonal) CPU ütemezésnél Hosszú-távú Középtávú Rövid-távú Egyéb erőforrás ütemezések is lehetnek Vadász
A sorbaállási modell Felfüggesztett Futásra kész Várakozó Hosszú-távú ütemezés CPU I/O Futásra kész Várakozó Felfüggesztett Belép Kilép I/O ütemezés Középtávú ütemezés Rövid-távú ütemezés Időszelete letelt Memória kérés M I/O kérés Vadász
Erőforrás csoportosítás1 A típus száma szerint Egy példányos erőforrások Többpéldányos erőforrások: használati értékükben azonosak a példányok) Használati mód szerint Megosztottan használhatók, melyek Állapota lementhető és visszaállítható, emiatt esetleg „elvehetők” a használótól (preemption lehetséges) Csak kizárólagosan használhatók, melyek Nem vehetők el a használótól csak „zökkenőkkel” (pl. vissza kell küldeni a használót egy korábbi állapotába, terminálni kell a használót) Vadász
Erőforrás csoportosítás2 „Létük” szerinti csoportosítás Előállítandó és fogyasztható (consumable) erőforrás Itt: előállítás – igénylés|kérés – használat forgatókönyv Újrahasználható erőforrás Itt: igénylés|kérés – használat – lemondás|felszabadítás forgatókönyv … A forgatókönyvekhez Igénylés, kérés: esetleg várakozással Általánosan: egy kéréssel több erőforrásból, több példányt is igényelünk … A felszabadítás|lemondás (szignálozás is) 2 típusa (legalább) Adott típusból valamennyit felszabadítom Adott foglaltakból adott példányokat szabadítok fel Az igénylés|kérés, a lemondás|felszabadítás absztrakt instrukciók a processz instrukciófolyamában … Miért jó nekünk az absztrakt instrukció modell? Mert egy instrukcióról intuitíve tudjuk, hogy atomi … És ez a kívánság, hogy egy kérés atomi legyen, valahogy bennünk van .. Vadász
A CPU erőforrás különleges … A (független) processz modellnél a CPU erőforrás használathoz preemptív ütemezésnél nincs kér-használ-lemond „instrukciókészlet” A CPU ütemezésben nem lesz holtpont probléma … Legfeljebb a „kooperatív” ütemezésnél van lemondás … A „User-level” szálakat a taszk saját maga ütemezi … Itt már lehet holtpont probléma … Miért tettem zárójelbe a független szót? Beláthatjuk, a CPU valahogy különleges. Míg más erőforrásokat kérünk-használunk-lemondunk, addig a CPU használatnál ezek a mozzanatok nem értelmezhetők … Hacsak a „request” állapotátmenttel való blokkolt állapotba billentést nem nevezzük „lemondásnak” … Vadász
Erőforrás-használat - holtpont Az (egy) egyszerű példa: P1 és P2 processzek; X és Y erőforrások … P1-ben kér(X); kér(Y) absztrakt insrukció-sor P2-ben kér(Y); kér(X) absztrakt insrukció-sor A kérés-kielégítés modelljei A kér-en egy processz „blokkolódhat” (vár kielégítésre) A kielégités modellje: kielégítésre váró szignált vár (eseményt vár), végül megkapja a szignált; valami kell adja a szignált Húzzuk alá: a kérésen a processz blokkolódhat … Foglalva várakozás: folyamat lefoglal erőforrást, ugyanakkor más erőforrásra vár … A kielégítés szignál érkezésével történik; a szignált valakinek küldenie kell; elmaradó szignál holtpontot, kiéhezést (starvation) okozhat. Nincs erőszakos erőforrás elvétel (az erőforrást fel kell szabadítani, újra kell gyártani). Vadász
Együttműködő processzek egy absztrakt problémája Termelő-fogyasztó (Producer-Consumer) probléma Vannak termelő folyamatok, melyek saját ritmusukban terméket (item, message) állítanak elő és behelyezik egy raktárba. Vannak fogyasztó folyamatok, melyek saját ritmusukban a terméket kiveszik a raktárból és felhasználják, fogyasztják Van korlátozott termék-raktár. Vadász
A termelő-fogyasztó probléma A termék ugyan lehet elfogyó erőforrás, de az előállítására várakozás a raktár üresség kezeléssel eliminálódhat A raktárra való korlátozás vonatkozhat: a raktár méretére (teli raktárba termelő nem rakodhat, üresből fogyasztó nem fogyaszthat); Ez megelőzési (precedencia) probléma. a raktár használatára (egy időben több termelő nem használhatja a raktárt, egy időben több fogyasztó nem használhatja a raktárt, egy időben csak egy folyamat, akár termelő, akár fogyasztó, használhatja a raktárt.) Ez kölcsönös kizárási probléma. Vadász
Az alap séma Precedencia Kölcsönös kizárás processz() { // kérelmező processz while (true) { nem-kritikus-szakasz1(); kérelem(); használat(); nem-kritikus-szakasz2() } processz() { // engedélyező ... if (van-kérelmező) engedélyezés(); .... processz() { while (true) { nem-kritikus-szakasz1(); kérelem(); használat(); felszabadítás(); nem-kritikus-szakasz2(); } Az elsőben jól láthatóan újrafelhasználható erőforrásrók van szó … kérés-használat-lemondás A második fogyaszthatóra is! Miért is? Valóban is? Vadász
Alapfogalmak1 Kölcsönös kizárás (Mutual Exclusion): Csak kizárólagosan használható, egypéldányos erőforrásokért vetélkedő processzek közül egy és csakis egy, több példányos erőforrásokért vetélkedő processzek közül kötött számú processz kapja meg a jogot egy erőforráspéldány használatára. A kölcsönös kizárásnak legyenek módszerei, eszközei, technikái. Vadász
Alapfogalmak2 Kritikus szakasz (Critical Section): a folyamaton belüli kódrész (valójában annak futási időszakasza), melyen belül a kölcsönös kizárást meg kell valósítani. Egy erőforráspéldány kizárólagos használatának (idő)szakasza. Vadász
Alapfogalmak3-5 Belépési szakasz (Entry Section) a folyamaton belül az a kódrész, ahol kéri az engedélyt a kritikus szakaszba való belépésre. A kérelem szakasza. Kilépési szakasz (Leave Section) az a kódrész, ahol elhagyja a processz a kritikus szakaszt. Az erőforráspéldány felszabadítása: jelzés más processzeknek az erőforrás felszabadulásáról. A folyamatoknak természetesen lehetnek nem kritikus szakaszaik is. Vadász
Kívánalmak1 Biztonsági (safety) kívánalom: Valósuljon meg a kölcsönös kizárás: ha processz kritikus szakaszában fut, más processz csak akkor léphessen be kritikus szakaszába, ha van még szabad erőforráspéldány. (Ugyanazon időben csakis korlátozott számú kritikus szakasz futhat.). Természetesen, ezalatt további processzek a belépési szakaszukban lehetnek (éppen az a fontos, hogy azon ne jussanak túl). Vadász
Kívánalmak2 Előrehaladási (progress) kívánalom: általában nem kritikus szakaszban és nem belépési szakaszban futó processz ne befolyásolja mások belépését. Ha egyetlen folyamat sincs kritikus szakaszában és vannak processzek a belépési szakaszukban, akkor csakis ezek vegyenek részt abban a döntésben, hogy melyik fog végül belépni. Ráadásul ez a döntés nem halasztható végtelenségig. Vadász
Kívánalmak3-4 Korlátozott várakozási (bounded waiting) kívánalom: ha egy processz bejelentette igényét a belépésre, de még nem léphet be, korlátozzuk ésszerűen azt, hogy egy másik processz hányszor léphet be. Egy processz se várakozzon a végtelenségig belépésre azért, mert egy másik újból bejelentve az igényét megint megelőzi. Ne legyen kiéhezés. Hardver és platform kívánalom: ne legyenek előfeltételeink a hardverre (sem a CPU-k típusára, számára, sem a sebességükre), a processzek számára, relatív sebességükre, az operációs rendszer ütemezésére stb. Vadász
A mutex alap séma processz() { while (true) { „Blokkolni” kell Kölcsönös kizárás „Blokkolni” kell „Szignáloz” Blokkolni? Ne jusson a CPU-hoz Busy waiting Állapotváltással processz() { while (true) { nem-kritikus-szakasz1(); kérelem(); használat(); felszabadítás(); nem-kritikus-szakasz2(); } Figyeljük, hogy fogunk „blokkolni”!!!! Vadász
0-ik megoldás: Megszakítás letiltás Alapalgoritmusok, építőkövek 0-ik megoldás: Megszakítás letiltás Kritikus szakasz elején IT letiltás, végén engedélyezés Csak egy CPU-ra jó (4. követelmény!) Veszélyes: hiba esetén deadlock! Csak nagyon letesztelt kernel kódokban! Ne jusson a CPU-hoz módon blokkolunk A kérés során egy processz leállítja a másik CPU-ját (nem ütemeződhet ki másik) Felszabadítás során engedélyezi a CPU-t (ez a szignálozás) Vadász
Megoldás #1: váltogatás Az osztott turn változó nyomonköveti, ki lesz a következő. shared turn=i; processz-i() { while (true) { nem-kritikus-szakasz1(); while(turn!=i) nop(); kritikus-szakasz(); turn = j; nem-kritikus-szakasz2(); } processz-j() { while (true) { nem-kritikus-szakasz1(); while(turn!=j) nop(); kritikus-szakasz(); turn = i; nem-kritikus-szakasz2(); } Tevékeny várakozással blokkolunk Sérül a 2. követelmény: nem követi, hogy egy processz érdekelt-e vagy sem (4. köv: csak 2 processzre). Vadász
Megoldás #1: turn & erd Egymás váltogatása az érdekeltség figyelembevételével Shared turn, erd[N] = {false, false, …}; processz-i() { while (true) { nem-kritikus-szakasz1(); erd[i] = true; turn = j; while (erd[j] && turn == j) nop(); kritikus-szakasz(); erd[i] = false; nem-kritikus-szakasz2(); } processz-j() { while (true) { nem-kritikus-szakasz1(); erd[j] = true; turn = i; while (erd[i] && turn == i) nop(); kritikus-szakasz(); erd[j] = false; nem-kritikus-szakasz2(); } Tevékeny várakozással blokkolunk Csak a 4. köv. sérül: 2 processzre jó. De kiterjeszthető! Vadász
Zárolásváltozó használat #2 Az osztott lock változó tesztelhető és beállítható. shared lock = false; processz-i() { while (true) { nem-kritikus-szakasz1(); while (tsl(&lock)) nop(); kritikus-szakasz(); lock= false; nem-kritikus-szakasz2(); } int tsl(*lock) { register int temp; temp = *lock; *lock = true; return temp; } // ez megszakithatatlan // instrukcio kell legyen Sérül a 4. követelmény: nem minden hardveren van ilyen instrukció. IT letiltás többprocesszoros rendszeren nem segít. A 3. követelmény is sérülhet: egy processz kisajátíthatja az erőforrást. Vadász
Szemaforok Dijkstra (1965) szemafor: pozitív egész számot tartalmazó közös változó és egy várakozási sor. Szemaforon két atomi operáció. Atomi: Egy időben csak egy processz operálhat egy szemaforon. Blokkolódásból “felébredő“ processz végrehajtja azt az operációt, amin előzőleg blokkolódott. DOWN (P: Passeren) - ezen blokkolódhat. UP (V: Vrijgeven [vrejhéfen]) - ez szignáloz. Figyeljünk fel az atomiságra! Vadász
Operációk szemaforon DOWN(semaphore S) { if (S >= 1) S=S - 1; } else { a hivo helyezze magat az S varakozo sorara és PC-t állítsd az if elé} } UP(semaphore S) { S= S + 1; if (S varakozo sora nem ures) {egy processzt vegy le rola} Vadász
Kritikus szakasz védelme Használata Kritikus szakasz védelme Szinkronizáció shared semaphore s = 1; shared semaphore s = 0; processz-i() { while (true) { nem-kritikus-szakasz1(); DOWN(s); kritikus-szakasz(); UP(s); nem-kritikus-szakasz2(); } processz-i() { // ui elozze uj-t ... ui(); UP(s); } processz-j() { DOWN(s); uj(); Vadász
Egy termelő-fogyasztó probléma Esettanulmány Több termelő folyamat van. Több fogyasztó folyamat van. Korlátozott a raktár mérete: N számú hely. Korlátozott a raktárhoz való hozzáférés: csak egy ki-berakó gép van (egy időben csakis egy termelő vagy fogyasztó használhatja a raktárt). Raktár “teliség“ és “üresség“ kezelendő. Belátható, legalább 3 szemafor kell: Ki-berakó gép védelmére; Üresség jelzésére; teliség jelzésére. Kizárás probléma Szinkronizáció Vadász
A termelő #define N 32 semaphore mutex = 1; semaphore ures = N; semaphore teli = 0; void producer ( ) { termek_t termek; while (1) { termek=gyart( ); // nem kritikus szakasz DOWN(ures); // üres rekeszre vár DOWN(mutex); // raktárba lép termeket_raktarba_tesz(termek); UP(mutex); // raktárból kilép UP(teli); // teli jelzést növeli } } Vadász
A fogyasztó #define N 32 semaphore mutex = 1; semaphore ures = N; semaphore teli = 0; void cosumer ( ) { termek_t termek; while (1) { DOWN(teli); // teli rekeszre vár DOWN(mutex); // raktárba lép termek=termeket_raktarbol_vesz( ); UP(mutex); // raktárból kilép UP(ures); // üresek száma nő } } Vadász
Egy kérdés Mi történne, ha a sorokat felcserélnénk? DOWN(teli); // teli rekeszre vár, vagy csökkenti DOWN(mutex); // belép a raktárba sorokat felcserélnénk? Vadász
A Unix szemafor mechanizmusa Gyors, kis átvihető információmennyiség Indirekt, OS kötődésű, többirányú A kapcsolódó rendszerhívások: semget( ) // készítés, azonosítás semctl( ) // kontroll, jellemzők lekérdezése, törlés semop( ) // operációkészlet végrehajtás A szükséges „beleértett” fájlok: #include <sys/ipcs.h> #include <sys/sem.h> Vadász
Dijkstra szemafor – Unix szemafor D nem definiálta a blokkolás implementációját A Usem: Blokkolásos jellegű, elemi szemaforok készlete van egyetlen szemaforban, elemi operációk készletének végrehajtása történik egyetlen (atomi) operációban, az elemi operációk nemcsak egységgel csökkenthetnek, növelhetnek (non-unitary), van nulla ellenőrzés is, kérésre az elemi operációk „visszavonódnak” (undo) kérésre nincs blokkolódás, bár kellene. Vadász
A rendszerhívások: semget( ) int semget(key_t key, int nsems, int flg); Ahol key (hexa) kulcs a külső azonosításhoz, nsems az elemi szemaforok száma a szemaforban flg készítéskor védelem, hozzáférés beállítás, azonosításkor létezés ellenőrzés, hozzáférés ellenőrzés Készít nsems elemi szemaforból álló szemafort az adott kulccsal és hozzáférésekkel, vagy azonosít létező szemafort. Visszatér a szemafor belső leírójával, vagy hibaértékkel. Készítés során az elemi szemaforok 0 értéket vesznek fel. Vadász
Az elkészült szemafor belső adatstruktúrái Az alap adatszerkezet: struct semid_ds { struct ipc_perm sem_perm; // hozzáférések struct sem *sem_base; // a 0. elemi szem. Pointere ushort sem_nsems; // az elemi szem.ok száma … } Ebből egy elemi szemafor adatai: struct sem { ushort semval; // pillanatnyi érték ushort semzcnt; // nullára váró processzek száma ushort semncnt; // növelésre várók száma pid_t sempid; // utolsó operáló processz Vadász
A rendszerhívások: semctl( ) int semctl( int sid, int semnum, int cmd[, union semunion arg]); Ahol sid a belső leíró, semnum az elemi szemafor indexe, cmd IPC_STAT állapot lekérdező, IPC_SET állapot beállító, IPC_RMID törlő parancs. A GETVAL, GETALL, GETPID, GETNCNT, GETZCNT szintén lekérdező, a SETVAL, SETALL beállító parancsok. arg a felhasználói címtartományhoz tartozó union változó vagy konstans, lekérdezésnél eredmény, beállításkor forrás. Vadász
A semctl( ) híváshoz Az elemi szemaforok 0 és nsems-1 közötti indexűek. Az i-edik elemi szemafor inicializálása: semctl(sid, i, SETVAL, init_value) Az i-edik elemi szemafor pillanatnyi értékének lekérdezése: value=semctl(sid, i, GETVAL); Az arg típusa: union semun { int val; // elemi szemafor érték ushort *array; // elemi értékek tömbjének ptr-e struct semid_ds *buf; // adatstruktúrák mutatója } arg; // egyes rendszerekben ez a sem.h fájlban … Vadász
Rendszerhívások: semop( ) int semop(int sid, struct sembuf *sops, unsigned nsops); Ahol: sid a szemfor leírója, sops pointer az elemi operációk tömbjéhez, nsops az elemi operációk száma. Egy elemi operációt a következő struktúra határoz meg: struct sembuf { ushort sem_num; // az elemi szemafor indexe short sem_op; // az elemi operáció short sem_flg; // operációt módosító jelző } Vadász
Az operációkészlet Egy operációkészletre példa: struct sembuf opkeszlet[4] = { 0, -2, 0, 1, 1, 0, 2, 2, 0, 0, 0, 0 }; És az operáció: semop(sid, &opkeszlet[0], 4); Vadász
A sem_op értéke Ha a sem_op < 0, akkor vár, amíg a semval ≥ |sem_op|, és semval+=sem_op; (Negatív sem_op → dekrementáció, down operáció.) Ha sem_op == 0 akkor vár, amíg semval == 0 lesz. (Null ellenőrzés.) Ha sem_op > 0, akkor semval+= sem_op; (Pozitív sem_op → inkrementráció, up operáció.) Vadász
A sem_flg módosító Lehet 0, vagy vagy kapcsolatban a következő makrók IPC_SEMUNDO A processz terminálódására az elemi opráció „visszaáll”. IPC_NOWAIT Ha nem sikerülne a dekrementáció (down), akkor abortál a semop( ) és a processz fut tovább. Mire jó ez? … while( 1) { if ( semop( ) ) { kritikus_szakasz( ); break} else valami_mas_hasznos( ); } Vadász
Egy példa: nsems=3, nops=4 sid 1 2 Kiindulás semval sid 1 2 Eredmény num op flag 1 2 3 -2 Vadász
Példaprogramok Nézzük a „szokásos” helyet! http://www.iit.uni-miskolc.hu/~vadasz/ GEIAL202/Peldaprogramok/ipc/sem Vadász
Az ebédelő filozófusok … Nézzék meg Tanenbaum megoldást (D szemaforral egész bonyolult. Tanulmányozható a GEIAL202 Ea6-ban is!) Elegáns megoldás U szemaforral Vadász
Ebédelő filozófusok „Megterítünk” (inicializálás) #define N 5 … sid = semget(key, N, 0666); for(i=0; i < N; i++) semctl(sid, i, SETVAL,1); /* inicializáljuk a tömböt 1-ekre */ Vadász
Ebédelő filozófusok Az i-edik filozófus programja … #define N #define i i #define BAL (i – 1) % N /* bal szomszéd */ #define JOBB (i + 1) % N /* jobb szomszéd */ static struct sembuf villakat[2] = { BAL, 0, 0, JOBB, 0, 0}; … while(1) { gondolkodom( ); villakat[0].semop = -1; villakat[1].semop = -1; semop(sid, villakat, 2); /* 2 villa fel */ nyam_nyam_spagetti( ); villakat[0].semop = 1; villakat[1].semop = 1; semop(sid, villakat, 2); /* 2 villa le */ } Vadász
Kölcsönös kizárás - szemaforok Operációs rendszerek Kölcsönös kizárás - szemaforok