2009-07-26 8 views
3

Ich habe eine kleine App geschrieben in C entwickelt, um auf Linux zu laufen. Ein Teil der App akzeptiert Benutzereingaben über die Tastatur und verwendet den nicht-kanonischen Terminalmodus, so dass er auf jeden Tastenanschlag reagieren kann.Linux Terminal Problem mit nicht-Canonical Terminal I/O App

der Abschnitt des Codes, den Eingang akzeptiert ist eine einfache Funktion, die wiederholt in einer Schleife aufgerufen wird:

char get_input() 
{ 
    char c = 0; 
    int res = read(input_terminal, &c, 1); 
    if (res == 0) return 0; 
    if (res == -1) { /* snip error handling */ } 
    return c; 
} 

Dies liest ein einzelnes Zeichen vom Terminal. Wenn innerhalb eines bestimmten Zeitraums keine Eingabe empfangen wird (angegeben durch den Wert c_cc [VTIME] in der termios-Struktur), gibt read() 0 zurück, und get_input() wird erneut aufgerufen.

Das funktioniert alles gut, außer ich vor kurzem festgestellt, dass wenn Sie diese App in einem Terminalfenster ausführen, und schließen Sie das Terminalfenster, ohne die App zu beenden, die App nicht beendet, sondern startet in eine CPU intensive Endlosschleife read() gibt kontinuierlich 0 zurück, ohne zu warten.

Wie kann ich die App ordnungsgemäß beenden lassen, wenn sie von einem Terminalfenster ausgeführt wird und das Terminalfenster geschlossen wird? Das Problem ist, dass read() niemals -1 zurückgibt, so dass die Fehlerbedingung nicht von einem normalen Fall unterscheidbar ist, in dem read() 0 zurückgibt. Die einzige Lösung, die ich sehe, ist einen Timer einzufügen und anzunehmen, dass ein Fehler vorliegt read gibt 0 schneller als die in c_cc [V_TIME] angegebene Zeit zurück. Aber diese Lösung scheint bestenfalls hacky zu sein, und ich hoffte, dass es einen besseren Weg gibt, mit dieser Situation umzugehen.

Irgendwelche Ideen oder Vorschläge?

Antwort

1

Fangen Sie Signale und setzen Dinge zurück, bevor Ihr Programm beendet wird? Ich denke, SIGHUP ist diejenige, auf die du dich konzentrieren musst. Eventuell einen Schalter im Signal-Handler setzen, wenn der Schalter eingeschaltet ist, wenn er von read() bereinigt und beendet wird.

0

Lesen sollte 0 auf EOF zurückgeben. I.e. Es wird nichts erfolgreich lesen. Ihre Funktion gibt in diesem Fall 0 zurück!

Was Sie tun sollten, ist Vergleichswert von gelesen mit 1 zurückgegeben und Prozess Ausnahme. I.e. Du hast nach einem gefragt, aber hast du einen bekommen?

Sie werden wahrscheinlich errno == EINTR behandeln, wenn -1 zurückgegeben wird.

 
char get_input() 
{ 
     char c = 0; 
     int res = read(input_terminal, &c, 1); 
     switch(res) { 
     case 1: 
       return c; 
     case 0: 
       /* EOF */ 
     case -1: 
       /* error */ 
     } 
} 
1

Sie sollten das Timeout mit Select und nicht mit Terminaleinstellungen behandeln. Wenn das Terminal ohne Timeout konfiguriert ist, gibt es bei einem Lesevorgang nie 0 zurück, außer bei EOF.

Auswahl gibt Ihnen die Zeitüberschreitung, und lesen gibt Ihnen die 0 beim Schließen.

rc = select(...); 
if(rc > 0) { 
     char c = 0; 
     int res = read(input_terminal, &c, 1); 
     if (res == 0) {/* EOF detected, close your app ?*/} 
     if (res == -1) { /* snip error handling */ } 
     return c; 
} else if (rc == 0) { 
    /* timeout */ 
    return 0; 
} else { 
    /* handle select error */ 
}