2 child processz nem bir egymassal

Fórumok

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

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

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

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.

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…

--

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)

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)

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)

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?

+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.