2 child processz nem bir egymassal

 ( sj | 2015. május 8., péntek - 14:57 )

Adott egy preforkolo, demonkent futo program, ami kereseket dolgoz fel. A kuldo oldal idonkent ugyanazt a kerest tobbszor is elkuldi (ezzel nem tudok mit kezdeni, ez adottsag), igy megesik az is, hogy az egyes child processzek parhuzamosan ugyanazt a kerdest akarjak feldolgozni.

Hogyan lehet ezt elkerulni? Pl. csinaljuk egy lock file-t, ami ha mar letezik, akkor elvileg hibara kell szaladjon:


fd = open(filename, O_CREAT|O_EXCL|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
if(fd == -1){
// hibakezeles
}

mehetunktovabb();

A kerdes az, hogy letezhet-e a csillagoknak olyan egyuttallasa, hogy 2 child processz eseten mindketto sikeresen vegre tudja hajtani az open()-t? (a filename erteke ugyanaz)

Mert emberunk azt mondja, hogy mindket child processz siman tovabb ment a mehetunktovabb() fuggvenyre. OK, finomitsuk a dolgot, probaljuk meg fcntl()-lel lockolni is. Azt mar tuti csak az egyik processz tudja sikeresen vegrehajtani:


fl.l_type = F_WRLCK;
fl.l_start = 0;
fl.l_whence = 0;
fl.l_len = 0;

fd = open(filename, O_CREAT|O_EXCL|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
if(fd == -1){
// hibakezeles
return 0;
}

rc = fcntl(fd, F_SETLK, &fl);
close(fd);

if(rc == -1){
// hibakezeles
return 0;
}

mehetunktovabb();

De emberunk meg mindig nem boldog, mert a jelek szerint az fcntl is sikerult mindket child processznek.

A kerdes az, hogy mit nezek be, amiert nem mukodik egyik fenti megoldas, ill. hogyan lehetne jobban megoldani a feladatot?

Az meg hozzatartozik a teljes kephez, hogy valojaban nem 2 child processz dolgozott ugyanazzal az adattal, hanem 4. Viszont a masik 2 rendben fennakadt az open()-en:


May 8 16:35:47 aaa[19263]: 40000000554c98950125ada4: touch e45e8285f8790d7e0e64a079cdbf88849f3c0b5ac09b2b945a0831b48a817557 OK (<1294450849.3240194.1431083151418.JavaMail.root@>)
May 8 16:35:47 aaa[19268]: 40000000554c98950024377c: touch e45e8285f8790d7e0e64a079cdbf88849f3c0b5ac09b2b945a0831b48a817557 FAILED (<1294450849.3240194.1431083151418.JavaMail.root@>), errno=17
May 8 16:35:47 aaa[19265]: 40000000554c989501330f6c: touch e45e8285f8790d7e0e64a079cdbf88849f3c0b5ac09b2b945a0831b48a817557 OK (<1294450849.3240194.1431083151418.JavaMail.root@>)
May 8 16:35:47 aaa[19270]: 40000000554c989502e22e9c: touch e45e8285f8790d7e0e64a079cdbf88849f3c0b5ac09b2b945a0831b48a817557 FAILED (<1294450849.3240194.1431083151418.JavaMail.root@>), errno=17

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Milyen filerendszer van alatta? (Csak mert pl. NFS felett ezek a dolgok nem mennek). Ezért esetleg inkább a POSIX szemafort ajánlom: http://linux.die.net/man/7/sem_overview

ext4, de az fcntl (a man szerint) megy nfs folott is. A (virtualis?) gepben csak lokalis diszk van amugy.

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

Az aszinkron feldolgozas nem opcio? Vagyis, a kliens bekuldi a kerest, ellenorzod formailag, majd visszavalaszolsz egy oket, kozben a kerest es a keresbol generalt md5/sha1 hash-t lerakod valahova, es a tenyleges feldolgozo pedig figyeli, hogy a queue-ban volt-e mar hasonlo hash-sel keres, ha volt, kihagyja, ha nem, feldolgozza.
--
Blog | @hron84
Üzemeltető macik

Az aszinkron feldolgozas nem opcio?

nem

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

A close() rogton le is csapja a lockot. Akkor kene csak elengedni az fd-t, amikor mar vegzett az adott processz a feldolgozassal.

Jaja, az a rész oké, ez elkerülte a figyelmem, az O_EXCL jobban zavar, hogy miért nem működik rendesen. Ott nem illenék versenyhelyzetnek lenni imho.

az O_EXCL jobban zavar, hogy miért nem működik rendesen. Ott nem illenék versenyhelyzetnek lenni imho.

en is abban a hitben voltam eddig, hogy az open csak 1 processz szamara sikerulhet.

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

Otlet: csak egy process/thread figyelje a bejovo kereseket, majd az delegalja tovabb a lekerdezest valamelyik tenyleges erdemi munkat vegzo szal fele. Igy viszonylag kiegyensulyozott marad a rendszer es egy process/thread viszonylag konnyen tudja ellenorizni hogy egy keres nem jott-e be veletlenul 2x.

mar azon gondolkozom, hogy a filenevet beteszem egy sql tablaba, ahol a megfelelo oszlop kb. char(64) not null unique :-) az tuti nem sikerulhet 2x is

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

Miert sql-be? Miert nem rakod bele egy tombbe? Picit gyorsabban tudnal benne keresni.

hogyan eri el azt egy masik processz?

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

ptrace :-)

nice try

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

O_CREATļO_EXCL-lel mennie kell (további flag-ek nélkül), ha emberünk szerint nem, akkor téved. Lehet, hogy véletlenül nem ugyanaz a fájlnév, vagy törlöd.

Amúgy ettől is van jobb, IPC a témakör neve. Semaphore, mutex, etc. Feltéve, hogy számít az igényesség és a performancia. Ha nem olyan fontos a performancia, akkor én nem is forkolnék, és onnan kezdve minden egyszerűbb.

Pl. man 3 sem_open, vagy http://code.qt.io/cgit/qt/qtbase.git/tree/src/corelib/kernel/qsystemsemaphore_posix.cpp

--

inkabb a mmap-ot probalom majd ki. Meglatjuk. Egylore atdizajnoltam, hogy a close csak a legvegen fusson le. Teszteles alatt a dolog...

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

Az mmap magában nem lesz elég, kell még hozzá valami IPC is. Amúgy meg lehet a parent processz felé megtartani egy socketot (socketpair-rel csinálja a szülő, és odaadja egy FD-n keresztül a gyereknek az ő végét), és azon az szülővel beszélgetni.

szerintem eleg a mmap, mert tobb processz is tudja map-pelni az adott file-t. Btw. nem a szulovel kell beszelni, hanem a tobbi child processzel, amelyek x keres mulva elhalnak, es ujak lesznek helyettuk.

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

Na, ilyenre több lehetőséged is van.

Egész OS szinten globális erőforrások:
Ha mindég egy adott korlátnál kisebb az adatod (főleg, ha fix méretű), inkább javaslom a POSIX message queue-t (a SysV-féle elég kulturálatlan imho).
Stream jellegű adatfolyamra egy named pipe (FIFO) kényelmesebb lehet.
SHM-et + POSIX semaphore-t csak akkor használnék, ha a feladat jellegéből adódik, hogy közös pufferbe kell dolgozniuk a processzeknek. Ellenkező esetben végül ott lyukadsz ki, hogy létrehoztad a saját message queue megvalósításodat SHM fölött.
Még egy érdekesség: ha inkább eljáráshívás alapú a történet, akkor SUN-RPC vagy D-BUS is szóba jöhet.

Mivel a szülő processz ugyanaz, így ezeket is használhatod:
Ha stream jellegű, akkor UNIX domain socket - ezt - mivel közös a szülő - gond nélkül tudod használni: szülőben fork() előtt létrehozod, gyermeké az egyik, szülőé a másik vég. És onnantól kezdve read()-write() függvényekkel használható.
Ha csak egyirányú a kommunikáció, akkor a pipe() is jó lehet ugyanerre a célra.

Egyik kedvenc témám a Unix/Linux/whatever IPC amúgy :-)

Egyik kedvenc témám a Unix/Linux/whatever IPC amúgy :-)

akkor mondd meg, hogy a fenti open() hogy sikerulhet 2 processz szamara is :-)

A processzek nem kozos pufferbe dolgoznak, igy az shm/szemaforok nem allnak kezre. A fentebb irt sql insert teszt azert merult fel, mert a child processzek amugy is masszivan dolgoznak sql-be. Szoval lehet, hogy marad az eredeti open() teszt (esetleg tmpfs-re teve?) + konfigbol bekapcsolhatoan az sql insert teszt is, es akkor nem kell ipc.

De ha mar message queue is szoba kerult, akar memcached-be is mehet egy iras: ha sikerult hurra, ha nem, akkor hibakezeles, kilepes.

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

O_EXCL hibára mutass kódot, mert valószínűleg abban van bug, nem a kernelben. Szemafor megy processzek között is (ld. IPC jelentése).

--

ime:


fd = open(state->message_id_hash, O_CREAT|O_EXCL|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
if(fd == -1){
if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: touch %s FAILED (%s)", sdata->ttmpfile, state->message_id_hash, state->message_id);
return ERR_EXISTS;
}
close(fd);

if(cfg->verbosity >= _LOG_DEBUG) syslog(LOG_PRIORITY, "%s: touch %s OK (%s)", sdata->ttmpfile, state->message_id_hash, state->message_id);

a topikinditoban irt logok az eredmeny.

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

> state->message_id_hash

Ez honnan jön?

az adott level message-id-jebol kepzek sha256-ot. Ez is szerepel a logban.

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

A lehetőségek:
- a fájlt törlöd, és utána fut le egy másik processz sikeresen
- trunc flag okoz nem várt működést
- a két processz eltérő mappában áll, ezért a relatív fájlnév más fájlra mutat
- láthatatlan szemét karakter a fájlnévben
- több szálon fut a child processz, és felülírják ugyanazt az fd változót

--

Az azért érdekelne, hogy az O_TRUNC miért is van benne...

jo kerdes. Leredukaltam erre: fd = open(state->message_id_hash, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);

mondjuk egy peldaprogramban (ami csak az open-t teszteli) rendben mukodik igy is, ugy is.

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

Mert mi az O_TRUNC? Ha létezik a file, akkor nullázza le a méretét. De te az O_EXCL-lel azt feltételezed, hogy nem szabad létezni a filenak. Csak nekem ellentmondásos a kettő?

jogos, mar kivettem (bar ez a mukodesben nem okoz problemat)

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

Ha már itt tartunk: kell-e neked az O_RDWR?

azt is kivettem, csak ennyi maradt: fd = open(state->message_id_hash, O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);

na most arra kernek egy hatarozott valaszt, hogy ha van 2 processzed, ami (szinte) ugyanakkor er ehhez a kodhoz, akkor az egyiknel az open()-nek garantaltan -1-et kell-e visszaadnia?

--
"Tudom én hogy nem biztonságos, de le van szarva az egész [...] A WoW jelszavam maradjon titokban csak az a lényeg!" (BlinTux)

Korábbi ismereteim alapján és utánaolvasva a témának: igen. Ezért tettem fel a filerendszeres, fileneves kérdéseket, meg kb. mindent.
Ha van egy olyan minimális kódod, ami ezt triggereli, az érdekelne.

Ha jól sejtem, csak a message hash a filenév. Ugyanabban a könyvtárban futnak a processeid?

egy readlink kimenet a programodban a /proc/self/fd/[megnyitott_fd]-re segíthet.

+1 csak meg kellene strace-írozni, mert kb 99.99% eséllyel user error, de 0.01% esélye van a kernel hibának is.

Persze lehet logolással is próbálkozni, az alábbi peremfeltételekkel:
1. közös logfájl
2. amit mindenki appendre nyit
3. a logrekordok rövidek (mondjuk max 512)
4. az írás sprintf+write útján történik
5. a bejegyzések elején pid és timestamp (mondjuk gettimeofday[microsec])
6. open előtt és után is log-írás, persze fájlnévvel, errnoval, mindennel
7. persze ugyanez a close-re és unlink-re is.