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.