Comment faites-vous pour ne pas bloquer les entrées/sorties de la console sur Linux en C?

Comment faire pour ne pas bloquer la console IO sur Linux / OS X en C?

27
demandé sur Flimm 2009-04-04 22:37:28

8 réponses

pas vraiment. Le TTY (console) est un appareil assez limité, et vous ne faites pas non-blocage I/O. ce que vous faites quand vous voyez quelque chose qui ressemble à non-blocage I/O, disons dans une application de malédictions/ncurses, est appelé raw I/O . Dans les entrées / sorties brutes, il n'y a pas d'interprétation des caractères, Pas de traitement d'effacement etc. Au lieu de cela, vous devez écrire votre propre code qui vérifie les données tout en faisant d'autres choses.

moderne C programmes, vous pouvez simplifier cela d'une autre manière, en mettant la console I/O dans un filetage ou processus léger. Alors l'E/S peut continuer de la manière habituelle de blocage, mais les données peuvent être insérées dans une file d'attente pour être traitées sur un autre thread.

mise à jour

voici un tutoriel malédictions qui couvre plus.

11
répondu Charlie Martin 2015-05-30 16:26:49

comme Pete Kirkham , j'ai trouvé cc.byexamples.com , et ça a marché pour moi. Allez-y pour une bonne explication du problème, ainsi que la ncurses version.

mon code devait prendre une commande initiale à partir d'une entrée standard ou d'un fichier, puis surveiller une commande d'annulation pendant que la commande initiale était traitée. Mon code est C++, mais vous devriez pouvoir utiliser scanf () et le reste où j'utilise la fonction d'entrée C++ getline ().

la viande est une fonction qui vérifie s'il y a une entrée disponible:

#include <unistd.h>
#include <stdio.h>
#include <sys/select.h>

// cc.byexamples.com calls this int kbhit(), to mirror the Windows console
//  function of the same name.  Otherwise, the code is the same.
bool inputAvailable()  
{
  struct timeval tv;
  fd_set fds;
  tv.tv_sec = 0;
  tv.tv_usec = 0;
  FD_ZERO(&fds);
  FD_SET(STDIN_FILENO, &fds);
  select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
  return (FD_ISSET(0, &fds));
}

cela doit être appelé avant toute fonction d'entrée stdin. Lorsque j'ai utilisé std:: cin avant d'utiliser cette fonction, elle n'est jamais retournée true again. Par exemple, main() a une boucle qui ressemble à ceci:

int main(int argc, char* argv[])
{ 
   std::string initialCommand;
   if (argc > 1) {
      // Code to get the initial command from a file
   } else {
     while (!inputAvailable()) {
       std::cout << "Waiting for input (Ctrl-C to cancel)..." << std::endl;
       sleep(1);
     }
     std::getline(std::cin, initialCommand);
   }

   // Start a thread class instance 'jobThread' to run the command
   // Start a thread class instance 'inputThread' to look for further commands
   return 0;
}

dans le thread d'entrée, de nouvelles commandes ont été ajoutées à une file d'attente, qui a été traitée périodiquement par le jobThread. Le inputThread ressemblait un peu à ceci:

THREAD_RETURN inputThread()
{
  while( !cancelled() ) {
    if (inputAvailable()) {
      std::string nextCommand;
      getline(std::cin, nextCommand);
      commandQueue.lock();
      commandQueue.add(nextCommand);
      commandQueue.unlock();
    } else {
        sleep(1);
    }
  }
  return 0;
}

cette fonction aurait probablement pu être dans main(), mais je travaille avec une base de code existante, pas contre elle.

pour mon système, il n'y avait pas d'entrée disponible jusqu'à ce qu'une nouvelle ligne soit envoyée, ce qui était exactement ce que je voulais. Si vous voulez lire chaque caractère tapé, vous devez désactiver le "mode canonique" sur stdin. cc.byexamples.com a quelques suggestions que je n'ai pas j'ai essayé, mais le reste a marché, donc ça devrait marcher.

17
répondu jwhitlock 2017-05-23 12:10:06

je veux ajouter un exemple:

#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>

int main(int argc, char const *argv[])

{
    char buf[20];
    fcntl(0, F_SETFL, fcntl(0, F_GETFL) | O_NONBLOCK);
    sleep(4);
    int numRead = read(0,buf,4);
    if(numRead > 0){
        printf("You said: %s", buf);
    }
}

Lorsque vous exécutez ce programme, vous avez 4 secondes pour fournir de l'entrée standard. Si aucune entrée trouvée, il ne bloquera pas et retournera tout simplement.

2 Exemples d'exécutions:

Korays-MacBook-Pro:~ koraytugay$ ./a.out
fda 
You said: fda
Korays-MacBook-Pro:~ koraytugay$ ./a.out
Korays-MacBook-Pro:~ koraytugay$ 
11
répondu Koray Tugay 2015-05-30 16:42:11

I bookmarked " Non-blocking user input in loop without ncurses " plus tôt ce mois-ci, j'ai pensé que je pourrais avoir besoin d'une entrée console non-blocking, non-tamponnée, mais je ne l'ai pas fait, donc je ne peux pas me porter garant pour savoir si cela fonctionne ou non. Pour mon usage, Je ne me souciais pas qu'il n'ait pas été saisi jusqu'à ce que l'utilisateur appuie sur Entrée, donc juste utilisé aio pour lire stdin.

6
répondu Pete Kirkham 2009-04-04 18:59:55
4
répondu Aziz 2017-05-23 11:54:56

utiliser ncurses

3
répondu soulmerge 2009-04-04 18:46:37

une autre alternative à l'utilisation de ncurses ou threads est d'utiliser GNU Readline , spécifiquement la partie de celui-ci qui vous permet de enregistrer les fonctions de callback . Le modèle est alors:

  1. Utiliser select() sur STDIN (entre autres descripteurs)
  2. quand select () vous dit que STDIN est prêt à lire, appelez readline's rl_callback_read_char ()
  3. Si l'utilisateur a entré une ligne complète, rl_callback_read_char appellera votre callback. Sinon, il reviendra immédiatement et votre autre code pourra continuer.
2
répondu Tyler McHenry 2009-08-14 23:37:17

pas tout à fait sûr de ce que vous voulez dire par "console IO" -- est-ce que vous lisez à partir de STDIN, ou est-ce une application de console qui lit à partir d'une autre source?

si vous lisez à partir de STDIN, vous devrez sauter fread() et utiliser read() et write(), avec poll() ou select() pour empêcher les appels de bloquer. Vous pouvez désactiver le tampon d'entrée, ce qui devrait amener fread à retourner un EOF, avec setbuf(), mais je n'ai jamais essayé.

0
répondu Don Werve 2009-04-04 18:45:12