[MEGOLDVA]noncanonical soros kezelés

Fórumok

Jó dolgom volt, eddig - mindig kanonikus soros programokat kellet írni, jól is működtek. De most nem.
A noncanonical, aszinkron soros kommunikációt próbálom összerakni, der valami nem stimmel (vagy csak én nem értem). A program vezérlése a liboop -on alapul - ez egy olyan kis szerkezet ami feleslegessé teszi azt, hogy a select, signal és timer kezeléseket kelljen alapból írni. Helyette "fellő" az ember egy amolyan esemény kezelő interfészt, és a különféle eseményekhez hozzárendelheted a megfelelő kezelő procedúrát, amit aztán callback szerűen meghív az esemény kezelő.
A soros (pl. /dev/ttyS0) O_RDWR | O_NOCTTY | O_NONBLOCK flagekkel kerül megnyitásra, majd az fcntl segítségével beállítom a FASYNC flaget.
A termios beállítások:
c_cflag = baud | CS8 | CLOCAL | CREAD;
c_iflag = IGNBRK | ICRNL;
c_oflag = 0;
c_lfalg = 0;
c_cc[VMIN] = 0;
c_cc[VTIME] = 0;

A SIGIO -t kezelő "művelet":
i_res = read(i_dev,cs_buff,sizeof(cs_buff);

Ha a túloldalról, jelenleg egy közönséges win hyperterminál elküldök valamit "beugrik" a SIGIO -ra húzott kezelőre és "beleragad" a read -be, csak akkor kezdi végezni a dolgát (pill. kinyomja az stdout -ba a vett karaktereket) ha "Entert" ütök a túloldalon. Miért ragad ez bele a readbe?

Hozzászólások

Bocs, megint én vagyok a butus :(
A signal handler read -jén túljut - az utána következő (egyenlőre, debug célú) fprintf(stdout, -ba ragadt bele ("\n" nélkül mit várok) :D
Viszont valami nem gömbölyű, a SIGIO handlerbe már úgy érkezem, hogy "Interrupted system call" hibám van :(
Na ez most hol keletekezik?

* Én egy indián vagyok. Minden indián hazudik.

Jogos, de ...
Itt nem egyszerűen a signal handler van kicsatolva, callback, de mégsem - sajnos csak a windows API ból tudok hasonlatot, az pedig asynchron proceudre call - ami ugyanabban a szálban/processzben fut, azaznem kell semaphor meg mutex. Tulajdonképpen sorbarakja, egy queue -ba a végrehajtandókat. Persze lehet hogy tévedek, de eddig hibanélkül működött. Ezért is használom ezt a libraryt - nagyon kényelmes.

* Én egy indián vagyok. Minden indián hazudik.

Nem ismerem ezt a library-t, de.

Őszinte leszek: még nem láttam SIGIO-alapon (jól) működő kódot, és a mindenféle versenyhelyzetek és egyéb signal-handlingből következő szopások miatt amikor egyszer régen elolvastam, hogy hogyan is kéne mennie ennek, hamar letettem róla.

O_NONBLOCK + select/epoll + read, ennyi kell, meg korrekt hibakezelés.

Viszont akkor ez szinkron nem?
Nézd, ott a példa program. Amióta Linux -alá is írok programokat, azóta ezekben még nem csalódtam. Információkat is általában fel lehet lelni, és egyre csak bővül :) Nem hiszem, hogy annyira megbízhatatlan lenne, csak a hibák végére kell járni. De ha lesz tapasztalat, megírom.

OFF:
(Sok éve írtam egy egyszerű TCP kliens/szerver és UDP unicast/multicast managert, mutithread, overlapped és iocompletionqueue használatával, megbízhatóan működik, számos olyan program alapját képezi ami 24 órából 24 órát működik win32 NT4.0 hajnala - XP -ig bezárólag. Újabb sok évvel később valami hasonlót láttam a codeproject -en,, mint nagyszerű újdonság :)
Ha rám hallgatsz, sok-sok hiba-info-debug üzenet és analízis, ez a megbízható szoftver titka. Mint programozó csak a klasszikusokat tudom idézni "sz'vás os sz'pás nálunk már szokás".)

* Én egy indián vagyok. Minden indián hazudik.

O_NONBLOCK, tehát aszinkron.

A signal kezelés versenyhelyzetekkel teletűzdelt aknamező. Lehet alkotni érdekes dolgokat signal handlerből, de az a helyzet, hogy mire végiggondolod, hogy hányféleképpen tud a programod rossz időrendben signalokat kapni, és azt hogyan kezeljed le, addigra megírod ugyanazt nem signal handlerből, "tisztább, szárazabb érzés".

Csak akkor működik, ha beállítod az fcntl(device,SETFL,FASYNC) különben meg sem szólal.
Egyenlőre, az én kis egyszerű igényemnek megfelel, működik.

Mindösszesen, annyit csinál (most), hogy felpiszkálja a sorost (Baud rate a 8n1 default), felpiszkálja az esemény kezelő hurkot, leveszi amit a konzolról (stdin) kap és kiküldi sorosra, illetve levesi amit a soroson kap kinyomja a konzolra (stdout).
így, ebben a formában nem egy nagy szám de mint alap elegendő, ha bonyolultabb protokoll kell rá lehet húzni.

* Én egy indián vagyok. Minden indián hazudik.

Csak akkor működik, ha beállítod az fcntl(device,SETFL,FASYNC) különben meg sem szólal.

Az lesz a SIGIO-s aszinkron megoldás, amiről te beszélsz.
Amiről én beszélek, az a nem SIGIO-s megoldás, ahhoz kell az O_NONBLOCK.
És van a szinkron megoldás, amikor a read() addig nem tér vissza, amíg nem jön adat.

Már nem emlékszem pontosan a liboop kódjára, de szerintem ez a callback azután hívódik meg hogy a dolgok lezajlanak.
Másutt nem is tudod ezeket az eljárásokat meghívni, amikor kiadod azt az utasítást, hogy "oop_sys_run(oop_sys)" addig amíg nem "lépsz ki" a verkliből - valamely esemény visszatérésébe nem teszed azt hogy OOP_HALT addig hívogatja a kijelölt esemény kezelő eljárásokat. Azokban aztán van minden.

(A multithread programjaimban egy FIFO -ba kerülnek először az üzenetek, majd egy külön thread "kürtöli szét őket".)

* Én egy indián vagyok. Minden indián hazudik.

http://www.cocoadev.com/index.pl?SignalSafety

Az fprintf belül simán feltételezheti, hogy amikor éppen fut az fprintf, akkor a közepén nem fogja semmi félbeszakítani (mondjuk egy signal), majd még mielőtt az előző fprintf lefutna, előtte ismét belehívni az fprintfbe.
Nem tudhatod, ha nincs odaírva a man page-hez, hogy async signal safe, hogy elviselné-e ezt az fprintf.

Érdekes, hiába törlöm, minden alkalommal a signal handler elején, minden újabb meghíváskor újra keletkezik. A dokss kicsit homályos, mintha ez nem lenne hiba (végül is úgy tűnik működik amit eddig csináltam) de nem szeretem az ilyesmit, biztos van rá megoldás, csak mi?

* Én egy indián vagyok. Minden indián hazudik.

o"o" csak egy gyors kerdes: mukodik-e ez a sigio alapu koncepcio egyatalan ha soros portol olvasol? valahogy nekem ugy remlik hogy az csak socket-ekre ment/megy linux alol, de ma'r reg volt.

Példa program az autentikus "Serial programming howto" -ból:
http://www.linuxdoc.org/HOWTO/Serial-Programming-HOWTO/x115.html#AEN144
Szinte szóról szóra ezt csinálom. Mint mondtam működik is, azaz ha az egyik oldalon beírok valamit az a másik oldalon kipotyog, már csak ezt az "Interrupted system call" hibát kellene kigyomlálnom - bár ha jól értem (és látom - minden karakterem meg van) akár figyelmen kívül is hagyhatnám. De nem szeretem az ilyen "víz alatti zátonyokat" - később mindig előjönnek valahol.

* Én egy indián vagyok. Minden indián hazudik.

Igen, láttam ezt mint megoldást - bár nem igazán értem, hogy ez most miről is szól. De hova kellene ezt beillesztenem? A signal handlerebe?
Megnézem a mant is ...
Ráadásul, mit is szakítok meg, és mit is indítok újra?
A vicc, hogy a liboop -al már több tucat kis "interfészt" gyártottam, amiben számos dologra vártam pl. soros (canonical), socket és stdin, meg fájlok. Fel lövi az ember a megfelelő eseményeket, és aztán elindítja a liboop esemény várókáját - oop_sys_run - és megy a verkli ...
Mindenfelé teleszoktam tűzdelni hiba ellenőrzéssel és üzenettel nem láttam még ilyet.

* Én egy indián vagyok. Minden indián hazudik.

A signal handlerebe?
nem, oda ahol a signal handlert beallitod. ha liboop-t hasznalsz, akkor passz, de ha kezzel hasznalod, akkor a sigaction() fv-ne'l kell ezt a flag-et megadni. mondom, nezd meg a `man 7 signal`-t, abban a ``Interruption of System Calls and Library Functions by Signal Handlers'' fejezet jol leirja hogy mikor adnak vissza a konyvtari fv-k (beleertve a read()-et is) EINTR-et, azaz azt, amit neked kiirt a valami.

az az igazsag, hogy jobban nem akrom osztani az e'szt... csak egy par gondolat: relative sok effajta programot csinaltam ma'r ugyan c-ben, de a filozofia mindig a ``nonblock rossz, e'rtem?'' volt; kerultem mint szege'ny ege'r a templomot. 1x hasznaltam csak SIGIO/SIGPOLL-t, mikor egy atmel atmegaxxx-re kellett egy pc/linux-on is futo emulator-reteget irni. ezelobbin nyilvan nincs nativ multiplexing (select()-nek megfelelo"), csak mezei irq-k, amit pc/linux-on a legegyszerubb egy signal() + sigio-val emulalgatni ;] noe's ebben a programba tettem bele egy ilyen ordas kommentet, tobb oranyi szivas utan:


/*
    we have to do tricks here: the sigio stuff works only for sockets, not
    for ordinary character devices (such as uarts). so we create a child
    process here that takes care of the uart and communicates with the
    main process/program/loop via a socket (created by this socketpair()).
    the sigio stuff works well with sockets since the nice old days.
...
*/

no, tenyleg jobban osztottam a kelletenel, rizsa, csakhat igy akkor 3 dolog lehetseges:
- anno egy tonna rtfm/google/stb utan is bene'ztem valamit e's/vagy a liboop olyat tud amirol almodni se mertem anno;
- tenyleg nem lehet megcsinalni rendesen tty-kra es ezert megy a szivas most nalad is;
- azota miota ezt csinaltam (2 e'v, kb) ma'r fejlodott annyit a kernel ill. posix-abb lett me'g inkabb, hogy mar megy a sigio parhuzamosan tty-kkal is...

bocsi meg1x, ha felrevezeto" volt amit irtam... A.

Minél tovább nézegetem annál kevésbé értem. Mit szakítok meg?
Az én részemen meghívom a liboop esemény kezelőkéjét, és legközelebb a (debug üzeneteim alapján) már a soros SIGIO kezelő rutinjában találom magam, és azzal, hogy megszakadt egy "system call". Lehet, hogy a debug üzenet küldésem "szakad" meg?

* Én egy indián vagyok. Minden indián hazudik.

Na ez már érdekes. Itt tipikusan nem erről van szó. A soros olvasó read elé (jobb híján) beszúrtam egy errno=0. Az utána következő read succest dobott.
Egyébként, ismétlem, minden művelet (read, később a close, setserio) hiba nélkül lefut. De ha bárhol, VAKTÁBAN (csak a "rend" kedvéért), kiolvasom az errno -t ("%m" - GNU kiterjesztés) kidobja hibának az EINTR -t.

* Én egy indián vagyok. Minden indián hazudik.

> De ha bárhol, VAKTÁBAN (csak a "rend" kedvéért), kiolvasom az errno -t ("%m" - GNU kiterjesztés) kidobja hibának az EINTR -t.

Az errno értéke csak akkor számít, ha hiba fordult elő (egy olyan rendszerhívásnál, ami állítja az errno-t). Ha nem fordult elő hiba, akkor az errno értéke bármi lehet (a hibátlanul lefutó rendszerhívások nem nullázzák az errno-t, és menet közben az errno bármilyen értéket felvehet).

Szóval a hiba az az, hogy akkor nézed az errno-t, amikor nem kéne.

Én arra tippelek, hogy a liboop select-et használ az események begyűjtésére, és ez a select hívás az, ami EINTR-al tér vissza. Ami persze nem hiba, hanem a dolgok természetes menete a liboop-ben.

Tudom, hogy nem ez volt a kérdés, de talán másnak is hasznos lehet: én a libev eseménykezelőt használom aszinkron soros kommunikációra (is) és tökéletesen működik. Ha jön a ttyS0-ról valami, akkor történik egy kernelen belüli szoftveres megszakítás és meghívja a callback függvényemet, amivel kiolvasom a soros portról a bejött cuccot. Tehát nem kell folyamatosan és fölöslegesen lekérdezgetnem, hogy "jött -e valami?". A libev tud select, poll, kqueue, inotify, stb. Szinte mindent lefed ebben a témakörben.

Köszönöm! Anno, úgy 5 évvel ezelőtt már azon a ponton voltam, hogy magam írok valamit. Aztán kiderült, hogy ebből van már azért néhány megoldás. Akkor a liboop tűnt a legkényelmesebb megoldásnak. Azóta is ezt használom.
Negatívumai közé azt sorolnám, hogy nem tudom megnézni milyen timer, és egyéb eseményeket regisztráltam, így ha kilépéskor ezek nincsenek visszatörölve akkor hibát dob, így abban sem vagyok teljesen biztos, hogy minden szutykot kivesz a rendszerből.
A másik, az eléggé gyér dokumentáció - tény hogy amióta összerakták nem igen kellet módosítani.
Talán még meg kell említeni, hogy nem thread safe - azaz nem reentrans! Van akinek ez lehet fontos - nem c++ szép, sima c (nekem pont így jó :)

* Én egy indián vagyok. Minden indián hazudik.

Ugyan ebben a cipőben jártam anno, de nekem nem sikerült megtalálnom a megoldást, így hát feltaláltam a spanyol viaszt és írtam egy nagyon lassú és több sebből vérző IO és timer eseménykezelőt... Egy végtelen while ciklus, rengeteg if és egy procikímélő usleep volt az alapja:) De legalább működött és tudtam rá építkezni. Megjegyzem, akkor még fogalmam sem volt, hogy a grafikus, ablakos alkalmazások mögött is valami ilyesmi ketyeg. Ezután találtam rá véletlenül a select-re, libevent-re, aztán jött még egy pár és végül a libev letisztultsága fogott meg nagyon. Kicsi és bazigyors, beágyazott rendszereken is jól teljesít. Nagyon jó a dokumentációja, logikus az API. Van egy kazal kezelője (watcher), pl. IO, timer, cron-szerű timer, signal, fork/wait/child processzek, file/directory watcher, stb. Thread-safe, minden egyes szálnak külön event loopja (főciklusa) lehet és a szálak tudnak egymásnak eseményt (ébresztőt) küldeni. Ezzel a módszerrel remekül külön lehet választani egy programban a feladatokat. Könnyen beágyazható más event loop rendszerekbe, pl. GTK. Szóval jóságos. Azt nem tudom, hogy mennyire lehet azt lekérdezni, hogy milyen kezelőket regisztráltál, de erre elvileg nincs is szükség, ugyanis a koncepció az, hogy az összes watcher objektumát (sima C-ben) neked kell nyilvántartanod és a kellő ponton felszabadítanod. Ha egy esemény inicializálásánál eldobod a visszaadott objektumot, akkor attól még a watcher működni fog, de többé nem fogod elérni, sem felszabadítani és ez hibás működéshez vezethet.

Ilyen módon a liboop is threadsafe lehet, minden threadnek lehet saját eseménykezelő hurka. A liboop (ha jól emlékszem) egyszerű láncolt listában tárolja a dolgait. Viszont, amióta ezt használom nemigen vágytam a multithread -re, elintézi a IO várakozásokat a liboop illetve a rendszer. Embedded dolgokban méhg nem tudtam kipróbálni, de maga a kód nem tűnik memória és erőforrás pazarlónak (a gyér dokumentáció miatt anno eléggé beleástam magam).
Nézegettem akkoriban a libev -et de valami akkor még hiányzott belőle - gondolom tovább fejlesztették :)

* Én egy indián vagyok. Minden indián hazudik.

Na ezzel most mit csináljak?
^c - vel állítom le a kis programomat, de az még "utolsó" leheletével ezt a konzolra is kiírja "^C" - de nem az stdout vagy stderr, csak úgy :(
Mi az ördögöt lehet ezzel csinálni?

* Én egy indián vagyok. Minden indián hazudik.

> ezt a konzolra is kiírja "^C"

http://linux.die.net/man/3/tcsetattr

ECHOCTL

(not in POSIX) If ECHO is also set, ASCII control signals other than TAB, NL, START, and STOP are echoed as ^X, where X is the character with ASCII code 0x40 greater than the control signal. For example, character 0x08 (BS) is echoed as ^H. [requires _BSD_SOURCE or _SVID_SOURCE]

Te ezt most miért kaptam?
Sem ECHO, sem ECHOCTL nem állítottam be - tehát nem tudom kikapcsolni. Szerintem ez a konzol tty vagy virtuális terminál (gnome, mert programot írni kényelmesebb X ben).
(A sorosra ez ki nem megy. A programom nem olvassa be, csak a ^C -ből eredő SIGINT -et kezeli le - kilép.)
Szóval, hogy ehet ezt eltüntetni?

* Én egy indián vagyok. Minden indián hazudik.

> Te ezt most miért kaptam?

Azért kaptad, mert a kiírás "echo"-zásnak tűnt.

> Szóval, hogy ehet ezt eltüntetni?

Mi az az "ezt"? [Minél pontosabban tudod megfogalmazni (releváns részleteket adva a jelenséghez), annál hamarabb kapsz használható tippet az eltüntetésére.]

Egyébként ha nem "echo", akkor a te programod írja ki. Keresd a "hibát" a saját forrásodban ...

Akkor még egyszer nekifutok.
A programomat jelenleg úgy lehet leállítani (csak) hogy Ctrl+c -t nyomok neki. Ilyenkor, a konzolra (amiből futtatom) kinyom egy "^C" ahol éppen tart a kurzor. Ezt a "^C" kijelzést szeretném valahogy eltüntetni.
Nem hiszem hogy az én kis programom, csinálná a dolgot, mivel ha az stdout -ot és az stderr -t elküldöm a /dev/null -ba, akkor is csinálja.
Hogy lehet ezt a konzol szolgáltatást letiltani, eltüntetni?

* Én egy indián vagyok. Minden indián hazudik.

Ez azért van, mert a terminál, amiből a programodat futtatod, olyan üzemmódban fut, amiben ezt a ctrl-c-t ő ki akarja írni (cooked mode). A kiírást a terminál végzi, nem a programod. Annyit tehetsz, hogy a terminálod üzemmódját átállítod, hogy ne írja ki.

man tcsetattr

Persze a dologhoz hozzátartozik, hogy ha átállítod a terminált, akkor sajnos kilépéskor vissza is kéne állítanod, különben "szopni fogsz" (TM), ugyanis a terminál beállításai a programod lefutása után is megmaradnak, és nem biztos, hogy a shellben is pont ezt szeretnéd.

Csak hirtelen eszembe ötlött, hogy mintha ilyen gondom régebben nem lett volna pedig, ezt a "technológiát" már sokszor felhasználtam. Beléptem egy Lenny gépbe és ot a Ctrl+C csak simán sort emel, nem írogat "^C" és egyebeket. Megnéztem Etch alól - nincs "^C". Sarge sem ...
Valami konzolsetup dolog van ami megváltozott a Squeeze -ben!?

* Én egy indián vagyok. Minden indián hazudik.

Lehet hogy fáradt vagyok? Tavaszi fáradtság, vagy elkaptam a kanyarót :(
A kis programomat ilyen klassz dolgokra használom:
$echo -en "\x04\x05\x06" | ./program d:/dev/ttyS2 b:9600 w:1
Konzolból, parancssorból szépen működik kimegy a "04 05 06" (windows binterm).
Ha ugyanezt belerakom egy shell scriptbe, az megy ki, hogy
'-en[ ]\x04\x05\x06' ez, most miért van :o[ Már megint lemaradtam valamiről?

* Én egy indián vagyok. Minden indián hazudik.

Na ezt az "apróságot" nem tudtam :( Az echo ha csak simán "beírom" a szkriptbe, akkor a belső echo -t használja, és az másként viselkedik "-e".
Ez most nem esett jól.

SZERK:
Még rosszabb. A Debian a /bin/sh -> dash - nem ismeri a "\x" szekvenciát!
Én meg ugye reflexből #!/bin/sh.

* Én egy indián vagyok. Minden indián hazudik.