SIGPIPE unix domain socketen

 ( wachag | 2012. május 14., hétfő - 22:15 )

Üdv,

Két programom kommunikál unix domain socket segítségével. Az az érdekesség, hogy egy bizonyos idő után az egyik program SIGPIPE hibát kap az íráskor.

A különböző fórumok azt írják, ez csak akkor lehetséges, ha lezárom a socketet.

A problémám a következő: jelenleg egy fia kódsor nincs a programomban, ami lezárná...

Mi lehet az oka?

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

Törölj ki a programodból _mindent_, ami nem szükséges a jelenség reprodukálásához, aztán kopizd be.

Ez sajnos nem megoldható, annál összetettebb a projekt egyik fele, a másik felének a jó részéről meg nincs forráskódom (kb. plugint fejlesztek).

Azért találtam érdekességet:
http://stackoverflow.com/questions/2235938/what-can-cause-a-spontaneous-epipe-error-without-either-end-calling-close-or-c

> egy fia kódsor nincs a programomban, ami lezárná...

Akkor csak 1 lehetőség van: az exit zárja le.

Fut még mind a kettő, amikor ez történik.

Közben olvastam olyat (a fentebbi stackoverflow-os fórumon), hogy tud az is okozni ilyet, hogy exec történik, és nincs beállítva az FD_CLOEXEC. Mondjuk én nem exec-elek.

Viszont a másik érdekes, ami okozhatja állítólag, és ez sajnos az én esetem is, hogy az egyik oldalon nagyon gyorsan termelem az adatokat, a másikon meg lassan fogasztom. És ez is bedöntheti az egészet.

Aztán persze lehet, hogy ki fog derülni, hogy valami egyszerű dolog van mögötte. Mindenesetre most érdekes problémának tűnik.

igen, mert queue overflow esetén ez fog történni, bár nem SIGPIPE-al kéne zárnia, ami részletkérdés.
Ami esetleg még megtörténhet (bár a nyelvet nem írtad le), de c-ben lazán meg tudod csinálni hogy felülírod valahol a socket memória területét.

Ha az előbbi a hiba, hogy overflow-olsz, akkor csak annyit tudsz tenni, hogy nyitsz egy szálat, ami folyamatosan olvassa befele az adatokat majd töltesz felfele egy puffert amiről az eredeti szálad csemegézik.

Idézet:
Viszont a másik érdekes, ami okozhatja állítólag, és ez sajnos az én esetem is, hogy az egyik oldalon nagyon gyorsan termelem az adatokat, a másikon meg lassan fogasztom. És ez is bedöntheti az egészet.

Nem, olyankor csak var az irassal (hacsak az open()-nek nem adtal O_NONBLOCK flaget). SIGPIPE csakis akkor jon, ha a pipe masik vegen nem figyel senki.

Elvileg a unix domain stateless nem? Marmint hogy nem kene varnia az irassal, hanem beirja, majd overflow-ol ha nagyon nem akar kiesni a masik oldalt.

Nem merem biztosra mondani, de nem tartom valoszinunek. Mindjart osszedobok egy tesztprogramot, aztan meglatjuk :-)

Elkeszult a tesztprogram, a fogado oldal egy socket()/bind()/listen()/accept()-bol, a kuldo oldal egy socket()/connect()/sendfile()-bol all, egy ~200k-s filet kuld at egyben ugy, hogy a fogado oldal csak masodpercenkent olvas egy-egy karaktert, es nincs semmi hibajelzes, szepen csordogalnak at a karakterek.

Thx a tesztet!;) a kuldo wait-el vagy a socket bufferel?

Nincs mit :-)

Hirtelen nem talaltam semmi leirast, de az lenne logikus, es a mukodesbol is ugy tunik, hogy a kernelben van egy queue, ahova megy az iras. Amikor az megtelik, a kuldo processz write()-ja blokkol, hacsak nem O_NONBLOCK modban nyitottad meg (akkor EWOULDBLOCK hibat kapsz).

Pipe-oknal (ami nagyon hasonlit a stream modu sockethez) egy 64k-s puffer van a kernelben, gondolom, itt is valami hasonlo meretu lesz.

Itt megtalalod a forrast:

unix-socket-reader.c
unix-socket-writer.c

Én ugyanezt most megcsináltam unix domain socket-tel (socketpair), ott is csordogálás megy. Szóval ez zsákutcának tűnik.

En is unix domain socket-et csinaltam, csak nem socketpair()-rel.

Mindenkeppen arrafele keresgelnek, hogy az olvaso oldal lezarja a kapcsolatot, mert SIGPIPE-ot maskepp nem kaphatsz.

Igen, már csak a hol a kérdés :-).

Az olvasó oldal egy Verilog VPI plugin. Az érdekes az, hogy iverilog-gal futtatva rendben működik minden, ha ModelSim-et használok (6.5b), akkor körülbelül a 17000-18000-edik olvasás tájékán leáll.

Csak nagyon erős bennem az a "nevelés", hogy a hiba az én kódomban van, ritka eset, hogy más okozza.

Idézet:
Igen, már csak a hol a kérdés :-)

Na igen... :-)

Idézet:
Az olvasó oldal egy Verilog VPI plugin

Ha jol sejtem, akkor ezt te irtad. Probalj meg letenni egy-egy breakpointot olyan helyekre, ahol "eszreveszed", hogy lezartad a kapcsolatot, hatha kiderul valami.

Idézet:
Csak nagyon erős bennem az a "nevelés", hogy a hiba az én kódomban van, ritka eset, hogy más okozza.

Lattunk mar sokmindent... ;-)

Biztatasul alljon itt az egyik gpsd fejleszto alairasa:

GDB has a 'break' feature; why doesn't it have 'fix' too?

;-)

Írj egy minimális méretű kódot, ami utánozza a "gyors termelő", "lassú fogyasztó" jelenséget, és akkor kiderül.

Hmm. Ravasz. Kipróbálom.

Nem, valószínűleg nem ez okozza.

Nos, nem úgy tűnik, mintha gond lenne.

> az egyik program SIGPIPE hibát kap az íráskor.

Akkor a "másik" zárja le. Pld ha az "egyik" 0 hosszú blokkot küld, akkor a "másik" hiheti, hogy vége a kapcsolatnak, és lezárja a socket-et, amire az "egyik" SIGPIPE-ot kap.

Nincs jobb ötletem.

Nekem sincs. Ha rájövök, mi okozza, megírom. A memóriaterület elrontását még lehetségesnek találom.

Anno en is belefutottam ilyesmibe, a megoldas egy signal(SIGPIPE,SIG_IGN) volt a program elejen, es akkor ennyi. Persze akkor jo ez, ha egyebkent a sigpipe-ot nem akarod hasznalni aktivabban ma'sra.

A `man` szerint sigpipe akkor fordulhat csak elo ha egy (masik oldal altal) lezart fd-be irsz bele, de ez (szerintem legalabbis) race condition nelkul nem ellenorizheto". Elvileg csinalhatsz ugyan egy select(fd+1,NULL,&wset,NULL,&tvnull); szintu" ellenorizest, ahol a &wset-be beteszed azt az egy szem fd-t, a &tvnull-ba pedig a (0,0)-s ido"tartamot - es akkor ez mogmondja ugyan hogy az fd irhato-e vagy sem (mindenfele mellekhatas, blocking vagy sigpipe nelkul). De ha ezutan jon a write(fd,...,...), akkor attol me'g lehet hogy a select() es a write() _kozott_ feluton szakadt es/vagy za'rt az fd ma'sik oldala.

Ha figyelmen kivul hagyod (SIG_IGN) a sigpipe-t, akkor a write() egy -1 / errno==EPIPE kombinacioval kell visszaterjen - tehat a helyzetet alapjabanveve ugyanugy tudod kezelni. Lasd me'g: `man 2 write`, "errors" szakasz.