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ások
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)
shm?
--
Blog | @hron84
Üzemeltető macik
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/qsystemsem…
--
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
--
Named mutex fájl helyett?
--
http://naszta.hu
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.