Linux IPC - II.¶
A gyakorlat anyaga¶
Ebben az anyagrészben először megnézzük, hogyan tudunk közös memóriaszegmenst létrehozni, amelyet minden létrehozott folyamat elér, ezt követően létrehozunk egy bináris szemafort, amellyel megvalósítjuk a kölcsönös kizárást és a szinkronizációt.
Közös memóriaszegmens létrehozása¶
Először az int shmget(key_t key, size_t size, int shmflg)
függvényt használjuk egy új memóriaszegmens létrehozására, amely a szegmens azonosítójával fog visszatérni (de lehetne használni egy már létező szegmenshez való hozzáféréshez is).
-
A
key
paraméter egy egyedi kulcs, amely azonosítója a szegmenst:IPC_PRIVATE
-tel inicializálva egy olyan új szegmens kerül létrehozásra, amely csak a hívó folyamatok - a szülő és az általa létrehozott gyerek folyamatok - között lesz érvényes, más külső folyamat nem fog tudni hozzáférni. -
A
size
paraméter az aktuális memórialap méretének többszöröséhez lesz felkerekítve (1 eseténgetconf PAGE_SIZE
méretre). -
Az
shmflg
a szegmens további konfigurációját állítja be, pl.IPC_CREAT
esetén egy új szegmens kerül létrehozásra, ha az adott kulccsal nem lett szegmens létrehozva.IPC_EXCL
esetén az előbbi esetben hiba dobódik. Ezenkívül még a jogosultság kerül beállításra, ami0666
esetén írási és olvasási jogosultságot jelent (lásd chmod parancs).
Ezt követően az void *shmat(int shmid, const void *shmaddr, int shmflg)
függvényt használjuk, amellyel egy osztott memóriaszegmenst tudunk a hívó folyamat címteréhez rendelni.
-
shmid
lesz a korábban létrehozott memóriaszegmens azonosítója (azaz ashmget
visszatérési értéke). -
shmaddr
paraméter egy memóriacímet vár, ahova a szegmenst szeretnénk becsatolni.NULL
-lal inicializálva egy szabad memórialaphoz csatolja a szegmenst (a rendszer választ). -
Tipikusan az
shmflg
jelző a0
értékre lesz inicializálva, ezáltal a szegmens olvasható és írható. De kaphatja azSHM_RDONLY
értéket is, ekkor ez a megosztott memória csak olvasható memóriaként lesz csatolva (tipikusan akkor, ha a hívó folyamatnak csak olvasási jogosultsága van) vagy lehetSHM_EXEC
, ekkor a szegmens tartalma végrehajtható jogosultságot kap.
A szükséges header fájlok: sys/types.h
, sys/shm.h
és sys/ipc.h
.
Ezen utasítások követően tehát az adott típusra cast-olt pointer a megosztott memóriaszegmensre fog mutatni a szülő és a gyerekfolyamatok esetében is.
A következő programban létrehozunk egy közös memóriaszegmenst szöveg tárolására (text
változó), amelyet aztán a gyerekfolyamat módosít (string.h
-ban elérhető függvény segítségével). A szülőfolyamat először bevárja a gyerekfolyamatot, majd kiírja a módosított szöveget bizonyítva, hogy a korábban létrehozott memóriaszegmens meg van osztva a szülő és a gyerekfolyamat(ok) között.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
Szemafor létrehozása¶
A szemafor definíciója egyszerű, lényegében olyan absztrakt adattípus, amellyel megvalósítható a kölcsönös kizárás és a szinkronizáció. Két része van, az egyik tárolja a szemafor értékét, a másik pedig egy várakozási sor. Amennyiben a szemafor értéke a 0-t és az 1-et veszi fel, úgy bináris szemafornak nevezzük, mi ilyen szemaforral fogunk foglalkozni. A szemaforhoz társított két művelet a wait(), illetve a signal(), ezeket tekintsük atominak. Ennek az az oka, hogy a wait és a signal művelet egy-egy kritikus szakasz a szemafor belső változóira nézve. Ezt a problémát egy tényleges szemafor implementációnál szoftveresen nem tudnánk megoldani, szükségünk van egy hardveres megoldásra, megszakíthatatlanul kiolvassa egy memóriarekesz tartalmát, majd beír oda egy másik értéket. Ezt test-and-set utasításnak nevezik:
A wait és a signal pszeudokódja a következő:
1 2 3 4 5 6 |
|
1 2 3 4 5 6 |
|
A folyamatinterakciókhoz tarozik még egy fogalom (amivel ebben az anyagrészben nem foglalkozunk), a holtpont. Ez egy nemkívánatos állapot, a szálak közötti körkörös függés esetén a program blokkolódik.
Tekintsük át és értelmezzük a korábbi pszeudokódok segítségével, hogy milyen esetei vannak a kölcsönös kizárásnak két folyamatra nézve:
Tekintsük át és értelmezzük a korábbi pszeudokódok segítségével, hogy milyen esetei vannak a szinkronizációnak két folyamatra nézve:
A szemafor létrehozására szolgáló int semget(key_t key, int nsems, int semflg)
függvény létrehoz egy szemaforhalmazt.
-
A
key
és asemflg
megegyzik a korábban látottshmget(..)
függvény paramétereivel -
A halmazban lévő szemaforok számát a
nsems
paraméter állítja be (1-re állítjuk, mivel 1 szeamforral szeretnénk dolgozni).
A int semctl(int semid, int semnum, int cmd, ...)
a szemafor inicializálását és állapotának lekérését végzi.
-
semid
: a szemaforhalmaz azonosítója. -
semnum
: a szemafor indexe a halmazban. -
cmd
: az elvégzendő művelet, pl. SETVAL esetén a szemafor kezdőértékét adjuk meg, GETVAL esetén az aktuális értékét kérjük le, de lehetőség lenne további szemaforjellemzőket is lekérdezni az IPC_STAT segítségével. -
...
: egy opcionális union típusú paraméter, amely az előző (cmd
) érték szerint lehet pl. egy érték (SETVAL-hoz) vagy egy eredmény-struktúra (IPC_STAT).
Az utolsó függvény, amit a szemafor működéséhez szükséges, a int semop(int semid, struct sembuf *sops, size_t nsops)
, aely a szemaforon végrehajtandó műveletet végzi.
-
sops
: ez egy struktúra vagy tömb, amely tartalmazza a művelet. A struktúrának része asem_num
, amely a szemafor indexét jelzi a halmazon belül,sem_flg
, amely blokkoló futást eredményez és csak akkor folytatódik a futás, ha a szemaforművelet befejeződött, és végül asem_op
, amely negatív paraméter esetén csökkenti, pozitív paraméter esetén növeli a szemafor értékét. -
nsops
: a struktúrák száma az előző paraméterben.
A szükséges header fájl: sys/sem.h
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
|
Az elkövetkező két példában először a szülő- és gyerekfolyamat közötti szinkronizációra, majd ezt követően a szülő- és gyerekfolyamat közötti kölcsönös kizárásra látunk szemléltető példát.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Bináris és nem-bináris szemafor¶
A nem-bináris szemafor értéke 0 és N között lehet, ahol N a kritikus szakaszba szabadon belépő és onnan kilépő folyamatok száma. Ezáltal ez nem garantálja a kölcsönös kizárást, mivel egyszerre több folyamat is beléphet a kritikus szakaszba. Viszont van, hogy erre van szükség, pl. az étkező-filozófusok probléma esetén. Linux IPC esetén közvetlenül nem tudunk nem bináris szemafort létrehozni.
Memóriaszegmens és szemafor felszabadítása¶
Az ipcs
utasítás szolgál arra, hogy kilistázzuk a már létrehozott megosztott memóriaszegmenseket és szemaforokat (a listában az shmget
és az shmget
függvények visszatérési értékeit kapjuk meg).
Ezeket szükség esetén törülhetjük:
1 2 |
|
Érdekesség¶
Fork Bomb néven híresült el az a szolgáltatásmegtakadással járó támadás (Denial of Service vagy DoS), amely során egy folyamat folyamatosan replikálja önmagát elérve ezzel, hogy a Linux alapú operációs rendszer kifogyjon a memóriából, illetve a CPU-t is folyamatosan terhelés alatt tartja. Amennyiben aktiválódott, úgy a rendszer teljes újraindítása szükséges, hiszen az összes létrehozott folyamatot egyidejűleg kellene megszüntetni.
Egy lehetséges kivédése a támadásnak, ha maximáljuk az adott felhasználó által létrehozható folyamatok számát.
1 2 3 4 5 6 7 |
|
Gyakorló feladat¶
Írj egy C programot ami Linux IPC-t illetve egy header fájlba kiszervezett szemafort használ. Hozz létre egy közös memóriaszegmenst egész számok tárolására és inicializáld egy pozitív számmal.
A szülő folyamat hozzon létre 10 gyerekfolyamatot (a gyerekfolyamatok szülője ugyanaz a folyamat legyen).
-
Ha a gyerekfolyamat azonosítója páros, akkor altasd a folyamatot 3 mp-ig, majd szorozd be szegmens értékét 3-mal.
-
Egyébként altasd a folyamatot 1 mp-ig és adj a szegmens értékéhez 7-et.
A szülő folyamat várja meg a gyerekfolyamatok terminálását, majd írja ki az eredményt. K ölcsönös kizárás segítségével biztosítsd, hogy a gyerekfolyamatok ne írják felül egymás műveleteit. Csak is kizárólag a szükséges kódrészlet legyen része a kritikus szekciónak.