14 votes

Comment puis-je implémenter le timeout pour read() lors de la lecture à partir d'un port série (C/C++) ?

Je lis des octets depuis un port série en C++ en utilisant un descripteur de fichier et la fonction posix/unix read(). Dans cet exemple, je lis 1 octet depuis le port série (les paramètres de vitesse de transmission et autres sont omis pour plus de clarté) :

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

int main(void)
{
   int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
   char buf[1];
   int bytesRead = read(fd, buf, 1);
   close(fd);
   return 0;
}

Si le périphérique connecté à /dev/ttyS0 n'envoie aucune information, le programme se bloque. Comment puis-je définir un délai d'attente ?

J'ai essayé de mettre un temps mort comme ça :

struct termios options;
tcgetattr(fd, &options);
options.c_cc[VMIN] = 0;
options.c_cc[VTIME] = 10;
tcsetattr(fd, TCSANOW, &options);

Je pensais qu'il était censé donner un délai d'une seconde, mais cela ne fait aucune différence. Je pense avoir mal compris VMIN et VTIME. À quoi servent VMIN et VTIME ?

Puis j'ai cherché sur le web et j'ai trouvé quelqu'un qui parlait de la fonction select(). Est-ce la solution et si oui, comment l'appliquer au programme ci-dessus pour faire un timeout de 1 seconde ?

Toute aide est appréciée. Merci d'avance :-)

23voto

Adam Rosenfield Points 176408

Oui, utilisez select(2) . Passez un ensemble de descripteurs de fichiers contenant juste votre fd dans l'ensemble de lecture et des ensembles vides d'écriture/exception, et passez un timeout approprié. Par exemple :

int fd = open(...);

// Initialize file descriptor sets
fd_set read_fds, write_fds, except_fds;
FD_ZERO(&read_fds);
FD_ZERO(&write_fds);
FD_ZERO(&except_fds);
FD_SET(fd, &read_fds);

// Set timeout to 1.0 seconds
struct timeval timeout;
timeout.tv_sec = 1;
timeout.tv_usec = 0;

// Wait for input to become ready or until the time out; the first parameter is
// 1 more than the largest file descriptor in any of the sets
if (select(fd + 1, &read_fds, &write_fds, &except_fds, &timeout) == 1)
{
    // fd is ready for reading
}
else
{
    // timeout or error
}

3voto

ceyun Points 173

A quoi servent VMIN et VTIME ?

Si MIN > 0 et TIME = 0, MIN définit le nombre de caractères à recevoir. avant que la lecture ne soit satisfaite. Comme TIME est égal à zéro, la minuterie n'est pas utilisée.

Si MIN = 0 et TIME > 0, TIME sert de valeur de temporisation. La lecture sera sera satisfaite si un seul caractère est lu ou si TIME est dépassé (t = TIME *0,1 s). TIME *0,1 s). Si TIME est dépassé, aucun caractère ne sera renvoyé.

Si MIN > 0 et TIME > 0, TIME sert de minuterie inter-caractères. La lecture de lecture sera satisfaite si des caractères MIN sont reçus, ou si le temps entre deux caractères dépasse TIME. La minuterie est redémarrée chaque fois que chaque fois qu'un caractère est reçu et ne devient actif qu'après la réception du premier caractère. caractère a été reçu.

Si MIN = 0 et TIME = 0, la lecture sera satisfaite immédiatement. Le site nombre de caractères actuellement disponibles, ou le nombre de caractères demandé sera retourné. Selon Antonino (voir contributions), vous pourriez émettre un fcntl(fd, F_SETFL, FNDELAY) ; avant la lecture pour obtenir le même résultat. le même résultat.

Source : http://tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html

1voto

vincent Points 138

Vous pouvez essayer de capturer le signal pour arrêter l'opération de lecture. Utilisez alarm(1) avant la lecture, et si la fonction de lecture n'est pas retournée, alarm enverra le signal SIGALRM, alors vous pouvez créer une fonction de traitement du signal pour capturer ce signal, comme ceci :

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>

static jmp_buf env_alarm;

static void sig_alarm(int signo)
{
    longjmp(env_alarm, 1);
}

int main(void)
{
   int fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY);
   char buf[1];

   if (signal(SIGALRM, sig_alarm) == SIG_ERR)
   {
       exit(0);
   }

   if (setjmp(env_alarm) != 0)
   {
      close(fd);
      printf("Timeout Or Error\n");
      exit(0);
   }

   alarm(1);
   int bytesRead = read(fd, buf, 1);
   alarm(0);

   close(fd);
   return 0;
}

Mais utiliser select ou poll ou epoll sera mieux si votre programme est grand.

0voto

Syphyreal Points 158

Select() est la façon dont je résoudrais ce problème.

Il y a plusieurs pages sur internet qui donnent des informations sur la façon d'utiliser select(), telles que http://www.unixguide.net/unix/programming/2.1.1.shtml

0voto

wallyk Points 33150

Il y a plusieurs approches possibles. Si le programme doit éventuellement chronométrer plus d'une opération d'entrée/sortie, select() est le choix évident.

Toutefois, si la seule entrée provient de cette entrée/sortie, le choix d'une entrée/sortie et d'une synchronisation non bloquantes est une méthode simple. J'ai étendu l'exemple d'une entrée/sortie à un seul caractère à une entrée/sortie à plusieurs caractères pour en faire un exemple plus complet :

#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <sys/time.h>

int main(void)
{
   int   fd = open("/dev/ttyS0", O_RDWR | O_NOCTTY | O_NDELAY);  // sometimes "O_NONBLOCK"
   char  buf[10];
   int   done = 0, inbuf = 0;
   struct timeval start, now;

   gettimeofday (&start, NULL);
   while (!done)
   {
       int bytesRead = read(fd, &buf[inbuf], sizeof buf - inbuf);
       if (bytesRead < 0)
       {
            error_processing_here();
            continue;
       }
       if (bytesRead == 0)  // no data read to read
       {
            gettimeofday (&now, NULL);
            if ((now.tv.sec  - start.tv_sec) * 1000000 + 
                 now.tv.usec - start.tv_usec > timeout_value_in_microsecs)
            {
                done = 2;    // timeout
                continue;
            }
            sleep(1);  // not timed out yet, sleep a second
            continue;
       }
       inbuf += bytesRead;
       if (we have read all we want)
           done = 1;
   }
   if (done == 2)
        timeout_condition_handling();
   close(fd);
   return 0;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X