Forum: PC-Programmierung select() und erkennen ob device noch lebt.


von Snowyrain (Gast)


Lesenswert?

Hallo,

ich habe ein kleines C-Program für Linux geschreiben. In der 
Hauptschleife wird mittels Select auf eine einkommende TCP-Verbindung 
und gleichzeitig auf Daten von der seriellen Schnittstelle gewartet. Das 
läuft auch super, ABER: wenn das Serielle-Device entfernt wird 
(USB-Serial-Adapter) weckt select permanent den Thread auf. Wenn ich die 
Daten abhole ist immer 1Byte im Buffer.

Ich erkenne das akt. in dem ich Zähle wie häufig 1Byte empfangen wurde. 
Ab einer Grenze wird das Device als nicht mehr vorhanden angesehen.Ich 
kann das Device nicht schließen und neu öffnen. Das gibt ein segfault 
wenn das Device weg ist.

Aber das wirkt auf mich unsauber. Kann ich irgendwie an den von Select 
veränderten Strukturen erkennen ob das Device noch lebt?
Oder gibt es hierfür eine andere Möglichkeit.

Gruß

Snowyrain

von Mark .. (mork)


Lesenswert?

Hallo Snowyrain,

bin zwar kein Linux-Experte, aber sollte select() nicht einen Fehler 
zurückgeben, wenn das Device entfernt wird?

MfG Mark

von Sven P. (Gast)


Lesenswert?

Hatte select() nicht auch ein FD-Set für Dateideskriptoren, die durch 
Fehler auffallen? Und für read() gibts doch errno-Werte.

von Snowyrain (Gast)


Lesenswert?

Hallo,

vielen Dank für die Antworten. exceptfds und errno stehen jetzt auf 
meiner zuerlernen Liste.

Vielen Dank

Snowyrain

von Snowyrain (Gast)


Lesenswert?

Hallo,

select und read sollten doch eigentliche -1 zurückliefern wenn etwas 
nicht stimmt? oder?!? Wenn ich das device entferne liefert select 
ständig 1 und read ständig 0. Und strerror(errno) liefert ständig ein 
"Success".

Irgendwie scheint select nicht mit entfernten Geräten klarzukommen.

Ich würde nach dem Select-Aufruf (, also nachdem select den Thread 
weckt,) überprüfen ob die Devices noch leben. Gibts dafür Befehle, eine 
Art Ping für Devices?!?

Gruß

Snowyrain

von Micky (Gast)


Lesenswert?

Hallo Snowyrain!

Von einem ping fuer devices ist mir nichts bekannt.

Vorschlag: select==1 und read==0 als Entfernen des Geraetes
interpretieren. Dann sofort schließen und aus der Selectliste 
rausnehmen,
anschließend eine Zeit lang abwarten, dann aber in bestimmten 
Zeitabständen versuchen, das device neu zu oeffnen.

von Snowyrain (Gast)


Lesenswert?

Hallo,

so habe ich es gemacht. Zudem habe ich den gesuchten "ping" gefunden. 
Ich frage die Attribute der seriellen Schnittstelle ab [tcgetattr()].

Vielen Dank

Gruß

Snowyrain

von Micky (Gast)


Lesenswert?

Danke Dir auch,

denn das mit dem tcgetattr() hatte ich vorher nicht so im Kopf.
Aber an welcher Stelle setzt Du es denn ein?
Ich vermute mal innerhalb des select(), bin mir da aber nicht sicher.

Gruß Micky

von Snowyrain (Gast)


Lesenswert?

Hallo,

termios.h stellt u.a. die Funktion tcgetattr() zur Verfügung. Ich frage 
hiermit einfach die Einstellungen des seriellen Ports ab. Wenn das 
fehlschlägt stimmt was mit der Schnittstelle nicht.

Ich belege select() mit einem Timeout. Zur Zeit alle 20Sek, ich werde 
später wohl auf >300Sek gehen. Jetzt wird der Errno-Wert und welchen 
Wert select zurück gibt überprüft. Zudem wird  tcgetattr() aufgerufen, 
als zusätzliche Kontrolle. Wenn etwas nicht stimmt wird die serielle 
Schnittstelle geschlossen und neu geöffnet.

Auf einem PC läuft das auch sehr stabil.

Gruß

Snowyrain

von Klaus W. (mfgkw)


Lesenswert?

Ich bin mir jetzt nicht ganz sicher, aber war das nicht so,
daß beim Schließen einer (Lese-) Verbindung select() aufwacht und
das Lesen eines Zeichens dann den Wert -1 alias EOF liefert
(als int, also von jedem tatsächlichen Zeichen unterscheidbar)?

Vielleicht hast du das Problem ja nur, weil du den von getc()
gelieferten Wert nicht als int mit EOF vergleichst, bevor du
ein char daraus machst?
Welches ist denn das eine Byte, das du beim Schließen bekommst?

Wenn es so ist, wie ich vermute, dann ist die Lösung relativ
einfach:
Wie gewohnt mit select() warten, wenn select() zurückkehrt
vom jeweiligen Device eine int mit getc() oder fgetc() lesen.
Falls diese int ==EOF ist, Datei schließen und vergessen.
Wenn nicht, dann zu char casten und als gelesenes Zeichen
verwenden.

von Klaus W. (mfgkw)


Lesenswert?

So, habe eben mal 'man 2 select' gelesen.
Es ist tatsächlich so, daß es das dokumentierte Verhalten von
select() ist:
Dateiende an einem Input-fd führt zu einem Rücksprung von select()
und ein anschließendes Lesen scheitert.
Genau das ist dann das Zeichen dafür, die Datei schließen und
vergessen zu können. Man muß sie natürlich dann auch aus dem
Satz der FDs für den nächsten select()-Aufruf entfernen und hat
dann seine Ruhe.

von Snowyrain (Gast)


Lesenswert?

Hallo,

dass mit dem EOF klingt gut. Ich werde es die Tage testen. Es würde 
meinen Code echt lesbarer machen. Und vor allem vernünftig.

Vielen Dank, ich melde mich wenn ich mehrt weiß

Snowyrain

von zwieblum (Gast)


Lesenswert?

das mit EOF ist eine kleine falle. wenn z.b. ein usb-seriell-adapter 
angeschlossen ist und du ein programm laufen hast, das das device offen 
hält und du dann den wandler abziehst, dann kehrt select() sofort zurück 
aber read() liefert 0 byte und sussess!

von Klaus W. (mfgkw)


Lesenswert?

EOF bekommt man doch nicht von read(), sondern wenn man getc()
oder fgetc() aufruft.
read() liefert im Fehlerfall 0 stattdessen, ja.

Ich baue zwar gerne Fallen, aber hier sehe ich keine.
Habe ich etwas übersehen? Sussess?

von zwieblum (Gast)


Lesenswert?

sorry, typo. read sollte irgendeinen fehler liefern, tut es aber nicht.

von Klaus W. (mfgkw)


Lesenswert?

Was lieferte denn dann? 0? Genau das ist der gemeldete Fehler!

Wenn select() für einen fd Lesebereitschaft signalisiert, das
anschließende Lesen aber nichts liefert (EOF bei getc()/fgetc(),
0 bei read()), dann ist das genau der Tip, den fd in die Tonne
treten zu können und die Datei zu schließen.

(Liefert read() nicht 0 (nichts gelesen) und auch nichts >0
(Anzahl gelesener Byte), sondern -1, dann ist das ebenfalls
als Fehler zu sehen und zusätzlich kann man errno auswerten
(und danach hoffentlich wieder zu 0 setzen).

Lt. snowyrain liefert aber select() 1 und read() 0, und genau
das ist doch das der erste Fall.)

aus man 2 read:
1
RETURN VALUE
2
       On  success,  the number of bytes read is returned (zero indicates end of file),
3
       and the file position is advanced by this number.  It is not an  error  if  this
4
       number  is smaller than the number of bytes requested; this may happen for exam‐
5
       ple because fewer bytes are actually available right now (maybe because we  were
6
       close  to  end-of-file,  or because we are reading from a pipe, or from a termi‐
7
       nal), or because read() was interrupted by a signal.  On error, -1 is  returned,
8
       and errno is set appropriately.  In this case it is left unspecified whether the
9
       file position (if any) changes.

Am Dateiende (bzw. nicht mehr existierender Verbindung, je nach Device)
wird 0 geliefert, und das nicht als Fehler betrachtet.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.