Forum: PC-Programmierung Problem mit select() und stdin


von Fritz (Gast)


Lesenswert?

Hallo,

ich verzweifle an einem Problem mit select().

Ich nutze select() um abzufragen ob Kommandos in stdin warten, die ich 
dann zeilenweise einlese und abarbeite. Das ganze funktioniert auch ganz 
gut solange nur ein Kommando wartet.

Warten mehrere Kommandos in stdin (also auch mehrere Zeilen) passiert 
folgendes:
- select() wird aufgerufen, gibt 1 zurück --> Kommandos warten
- die erste Zeile wird eingelesen und verarbeitet
- erneuter select()-Aufruf um auf weitere Kommandos zu prüfen
- select() gibt 0 (also keine weiteren Kommandos) zurück

Wenn ich stdin zeichenweise einlese liefert select() solange 1 bis ein 
Zeilenumbruch gelesen wird.

Hier nochmal ein Minimalbeispiel:
1
tv.tv_sec = 2;
2
tv.tv_usec = 0; 
3
FD_ZERO(&fds);
4
FD_SET(STDIN_FILENO, &fds); 
5
ret = select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); // gibt 1 zurück
6
fgets(buffer, 1024, stdin);
7
8
tv.tv_sec = 2;
9
tv.tv_usec = 0; 
10
FD_ZERO(&fds);
11
FD_SET(STDIN_FILENO, &fds); 
12
ret = select(STDIN_FILENO+1, &fds, NULL, NULL, &tv); // gibt 0 zurück, auch wenn noch zeilen in stdin warten
13
14
fgets(buffer, 1024, stdin); // <-- liefert nicht blockierend nächste Zeile

Ich würde ja alle Zeilen in stdin einfach auslesen und puffern, aber wie 
soll ich rauskriegen wieviele Zeilen in stdin warten um kein 
blockierendes fgets() aufzurufen.

Das ganz passiert unter Red Hat Linux 4.1.2-46 (Kernel 
2.6.18_164.11.1.e15)

Schonmal vielen Dank für eure Hilfe!

von Εrnst B. (ernst)


Lesenswert?

select und die stdio-Funktionen lassen sich nicht gut mischen, da 
fgets&Co einen eigenen Buffer-Layer mitbringen, über dessen Zustand 
"select" nichts weiß.

=> Wenn select, dann "read" statt fgets

von Rolf Magnus (Gast)


Lesenswert?

Außerdem kehrt select mit zurück, wenn Daten verfügbar sind. Das muß 
aber nicht zwingend eine ganze Zeile sein. fgets() wartet aber, bis eine 
komplette Zeile da ist.

Fritz schrieb:
> Ich würde ja alle Zeilen in stdin einfach auslesen und puffern, aber wie
> soll ich rauskriegen wieviele Zeilen in stdin warten um kein
> blockierendes fgets() aufzurufen.

Nomalerweise läuft es nicht so, daß die gesamte Eingabe schon im Puffer 
wartet und man die dann komplett auf einmal einliest, sondern du 
wartest, bis die Daten, die du erwartest, angekommen sind.

von Fritz (Gast)


Lesenswert?

Εrnst B✶ schrieb:
> select und die stdio-Funktionen lassen sich nicht gut mischen, da
> fgets&Co einen eigenen Buffer-Layer mitbringen, über dessen Zustand
> "select" nichts weiß.
Das mit dem eigenen Buffer bei fgets()&Co habe ich nicht gewusst. read() 
löst das Problem. Vielen Dank!


Rolf Magnus schrieb:
> Außerdem kehrt select mit zurück, wenn Daten verfügbar sind. Das muß
> aber nicht zwingend eine ganze Zeile sein. fgets() wartet aber, bis eine
> komplette Zeile da ist.
Da die Standardeingabe zeilengepuffert ist, kommen die Daten dort immer 
zeilenweise an. Oder irre ich mich da?

von Konrad S. (maybee)


Lesenswert?

Fritz schrieb:
> Da die Standardeingabe zeilengepuffert ist, kommen die Daten dort immer
> zeilenweise an.

Kann man so nicht sagen. "Zeilenweises Lesen" ist eine Eigenschaft der 
Funktion fgets(). "Puffern" ist eine Eigenschaft der C-Library (standard 
input/output library functions). select() und read() sind Systemaufrufe 
und passen zwar zueinander, aber eben nicht so ohne weiteres zu fgets() 
usw.

Für Unix/Linux hängt das Puffern auch vom Datei-Typ ab. Für reguläre 
Dateien ist die Standard-Puffergröße gerne sowas wie 8192 Byte ("block 
buffered" oder "fully buffered"), zumindest aber passend zur Blockgröße 
des Dateisystems. Für sowas wie Terminals oder Sockets macht das wenig 
Sinn. Hier wird "line buffered" verwendet, wenn es denn zeilenorientiert 
zugeht oder schließlich "unbuffered", also zeichenorientiert. Siehe auch 
"man setbuf".

Im beschriebenen Fall könnte man ganz auf select() verzichten und nur 
mit fgets() arbeiten, wenn man das Einlesen in einen eigenen Thread 
packt. Da gibt es dann eben andere Fallstricke...

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.