2009-07-26 8 views
3

J'ai une petite application écrite en C conçue pour fonctionner sous Linux. Une partie de l'application accepte les entrées utilisateur du clavier et utilise un mode terminal non-canonique pour pouvoir répondre à chaque frappe.Problème lié au terminal Linux avec une application d'E/S Terminal non-Canonical

La section de code qui accepte l'entrée est une fonction simple qui est appelée à plusieurs reprises dans une boucle:

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; 
} 

Ce lit un caractère unique du terminal. Si aucune entrée n'est reçue dans un certain laps de temps (spécifié par la valeur c_cc [VTIME] dans la structure termios), read() renvoie 0 et get_input() est appelé à nouveau. Cela fonctionne très bien, sauf que j'ai récemment découvert que si vous exécutez cette application dans une fenêtre de terminal, puis fermez la fenêtre du terminal sans terminer l'application, l'application ne quitte pas, mais se lance dans une boucle infinie intensive CPU, où read() renvoie continuellement 0 sans attendre.

Alors, comment puis-je quitter l'application gracieusement si elle est exécutée à partir d'une fenêtre de terminal, puis la fenêtre du terminal est fermée? Le problème est que read() ne renvoie jamais -1, donc la condition d'erreur est indiscernable d'un cas normal où read() renvoie 0. Donc la seule solution que je vois est de mettre un timer, et supposer qu'il y a une condition d'erreur si read renvoie 0 plus vite que l'heure spécifiée dans c_cc [V_TIME]. Mais cette solution semble au mieux, et j'espérais qu'il existe une meilleure façon de gérer cette situation.

Des idées ou des suggestions?

Répondre

1

Attrapez-vous des signaux et réinitialisez-les avant la fin de votre programme? Je pense que SIGHUP est celui sur lequel vous devez vous concentrer. Éventuellement définir un commutateur dans le gestionnaire de signal, si le commutateur est activé lors du retour de read() nettoyer et quitter.

0

Lecture doit retourner 0 sur EOF. C'est à dire. il ne lira rien avec succès. Votre fonction retournera 0 dans ce cas!

Ce que vous devez faire est de comparer la valeur renvoyée par read avec 1 et l'exception de processus. I.e. vous en avez demandé un, mais en avez-vous un?

Vous devrez probablement gérer errno == EINTR si -1 est renvoyé.

 
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

Vous devez gérer le délai d'attente avec les paramètres de sélection plutôt qu'avec les paramètres du terminal. Si le terminal est configuré sans temporisation, il ne retournera jamais 0 sur une lecture sauf sur EOF.

Sélectionnez vous donne le délai d'attente, et lire vous donne le 0 à la fermeture.

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 */ 
} 
Questions connexes