Ebben a részben arról lesz szó, hogy a UNIX milyen szolgáltatásokat
nyújt párhuzamos folyamatok fájlhozzáférésének a szinkronizálására.
A UNIX a fájlok konkurrens kezelésekor kialakult problémás
helyzeteket
a korábban már ismertetett rekord-lefoglalás lehetoségét
biztosítja. A rekord-lefoglalás az fcntl() rendszerhívás
segítségével implementálható a következokben
leírtak szerint (az fcntl() használatához szükség van az
<fcntl.h>
fájl include-olására is).
A UNIX rekord-lefoglalása kétféle lehet: írási vagy olvasási célú. A ketto közötti lényeges különbség az, hogy egy rekordterületet (azaz a fájl egy bizonyos intervallumát) írás céljából egyszerre legfeljebb csak egy folyamat foglalhatja le, és csak akkor, ha azt a részt senki más nem foglalta még le sem írási sem pedig olvasási szándékkal); továbbá meg kell említeni, hogy olvasási célú rekordterület lefoglalást egyidejuleg több folyamat is kérheti akár ugyanarra a rekord-területre is. Fontos tudni, hogy a párhuzamos folyamatok kölcsönös kizárása az fcntl() rendszerhívásnál valósul meg, és nem a fájlmuveleteknél: vagyis ha valamelyik folyamat nem tartja be a rekord-lefoglalási szabályokat, akkor az operációs rendszer megengedi neki, hogy kedve szerinti módosításokat illetve olvasásokat végezzen a fájlon, de az eredmény korrektségét ilyenkor nem lehet garantálni.
Az fcntl() rendszerhívás a következoképpen használható:
struct flock lock_data; int fd, cmd, rc; ... rc=fcntl(fd,cmd,&lock_data);
A rendszerhívás hatására az fd fájldeszkriptorral
azonosított fájl lock_data
argumentumban megadott része
a cmd argumentumban megadott módon lefoglalásra kerül.
A rendszerhívás írási célú rekord-lefoglalás esetén
sikertelen lesz, ha a fájlra nincs írási engedélyünk.
A rekordlefoglalás módja (a cmd argumentum értéke alapján) háromféle lehet:
F_GETLK
:
ennek hatására a lock_data
argumentumban kijelölt
részre vonatkozó rekord-lefoglalási információkat
kapphatjuk vissza (az operációs rendszer ekkor kitölti az
aktuálisan érvényes rekord-lefoglalási információi
alapján a lock_data
struktúra megfelelo mezoit --
ld. késobb).
F_SETLK
: ennek hatására az operációs
rendszer módosítja a fájlra vonatkozóan tárolt
rekord-lefoglalási információit, vagyis ezzel lehet egy-egy
rekordterületet lefoglalni vagy egy korábban kért
lefoglalási kérelmet hatástalalnítani. A lock_data
struktúra ltype
komponensének értéke
alapján a következo rekordlefoglalást végezhetjük el:
F_RDLCK
: ha ezt az értéket állítjuk be, akkor
olvasási céllal foglalhatjuk le a többi strukturakomponensben
specifikált fájl-rekordot.
F_WRLCK
: ha ezt az értéket állítjuk be, akkor
írási céllal foglalhatjuk le a többi strukturakomponensben
specifikált fájl-rekordot.
F_UNLCK
: ha ezt az értéket állítjuk be, akkor
a többi strukturakomponensben specifikált fájl-rekordra
vonatkozóan a korábbiakban kiadott rekord-lefoglalási
kérelmet vonhatjuk vissza, érvényteleníthetjük.
F_SETLKW
: ez ugyanazt teszi, mint az F_SETLK
, de
ez a folyamatot megvárakoztatja olyan esetben, amikor a
rekord-lefoglalási igénye nem kielégítheto. A folyamat
egészen addig vár, amíg a
rekordlefoglalási igényei ki nem elégíthetok, majd a
rendszerhívás visszatérésekor a megfelelo
rekordlefoglalási igényeket az operációs
rendszer érvényesítette.
Most áttekintjük a lock_data
argumentum egyes
komponenseinek a szerepét (vagyis azt, hogy az flock
struktúra egyes komponensei mit hogyan írhatnak le):
struct flock { short l_type; short l_whence; off_t l_start; off_t l_len; pid_t l_pid; };
l_type
: értéke a korábbiakban írtak alapján
F_RDLCK
vagy F_WRLCK
vagy pedig F_UNLCK
lehet.
l_whence
: értéke
SEEK_SET
vagy SEEK_CUR
vagy SEEK_END
lehet aszerint,
hogy az l_start
argumentum kezdopozíciójaként
a fájl elejét vagy az aktuális fájlpozíciót vagy
pedig a fájl végét kell-e tekinteni.
l_start
: értéke a lefoglalandó rekordterület
kezdopozícióját adja meg.
Ez egy byte-ban mért offszet az l_whence
-hez
mind kezdopozícióhoz képest.
l_len
: itt kell megadni a lefoglalandó rekordterület
hosszát byteokban mérve. Ha itt 0 értéket adunk meg, akkor
a fájl végéig az egész fájl tartalmát foglaljuk le, akár
a késobb hozzáfuzött adatokkal együtt is.
l_pid
: az F_GETLK
itt adja vissza a
többi komponensben specifikált rekordterületet lefoglaltan
tartó folyamat azonosítóját.
A rekordlefoglalás használatára lássuk a következo példát:
#include <fcntl.h> #include <stdio.h> main(int argc, char **argv) { struct flock lock_data; int fd, cmd, rc, rf; fd=open("probafile",O_RDWR); rf=fork(); lock_data.l_whence=SEEK_SET; lock_data.l_start=0; lock_data.l_len=10; if (rf==0) { lock_data.l_type=F_RDLCK; rc=fcntl(fd,F_SETLK,&lock_data); /* gyermek */ fprintf(stderr,"Gyermek tovabbfutott!\n"); sleep(5); lock_data.l_type=F_UNLCK; rc=fcntl(fd,F_SETLK,&lock_data); /* gyermek */ } else { sleep(1); fprintf(stderr,"Szulo: elkezd futni a lock elott ...\n"); lock_data.l_type=F_WRLCK; rc=(-1); while (rc==(-1)) { rc=fcntl(fd,F_SETLK,&lock_data); /* szulo */ if (rc==(-1)) fprintf(stderr,"Szulo: SETLK sikertelen ... ujraprobalom\n"); sleep(1); } fprintf(stderr,"Szulo lockolt es tovabbfutott!\n"); } close(fd); }
A program elindulása elott hozzunk létre egy probafile nevu fájlt, amire van írási jogunk (én ezt úgy tettem, hogy a gépem jelszófájlját átmásoltam erre a névre abba a directoryba, ahol a programot futtatni akartam).
A program elindulása után kettéágazik: szül egy gyermekfolyamatot. A gyermek lefoglalja olvasási céllal a probafile fájl elso 10 karakterét, és vár 5 másodpercig, mialatt a lefoglalást fenntartja, majd 5 másodperc után a lefoglalást megszunteti, majd lezárja a fájlt. A szülo vár 1 másodpercet, majd ciklusban másodpercenként egyszer megpróbálja lefoglalni a fájl elso 10 karakterét írási céllal.
A program futtatásakor a következoket írja ki a szabványos hibacsatornára:
Gyermek tovabbfutott! Szulo: elkezd futni a lock elott ... Szulo: SETLK sikertelen ... ujraprobalom Szulo: SETLK sikertelen ... ujraprobalom Szulo: SETLK sikertelen ... ujraprobalom Szulo: SETLK sikertelen ... ujraprobalom Szulo lockolt es tovabbfutott!
Ez értheto, mert a szülo folyamat 1 másodpercet vár, majd utána próbálja meg lefoglalni 1 másodpercenként a fájlt, és ez csak azután sikerül, miután a gyermeke a fájlt már nem foglalja olvasási céllal.