Подтвердить что ты не робот

Как я могу реализовать таймаут для чтения() при чтении из последовательного порта (C/С++)

Я читаю байты из последовательного порта в С++, используя файловый дескриптор и функцию posix/unix read(). В этом примере я читаю 1 байт из последовательного порта (настройки скорости передачи и аналогичные для ясности опущены):

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

Если устройство, подключенное к /dev/ttyS 0, не отправляет какую-либо информацию, программа зависает. Как установить тайм-аут?

Я попытался установить тайм-аут следующим образом:

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

Я думал, что он должен дать 1 секунда тайм-аута, но это не имеет значения. Я думаю, что неправильно понял VMIN и VTIME. Для чего используются VMIN и VTIME?

Затем я просмотрел веб-сайт и нашел, что кто-то говорит о функции select(). Это решение, и если да, то как можно применить это к программе выше, чтобы сделать 1-й тайм-аут?

Любая помощь приветствуется. Заранее спасибо: -)

4b9b3361

Ответ 1

Да, используйте select(2). Перейдите в набор дескрипторов файлов, содержащий только ваш fd в наборе чтения и пустых наборах записи/исключения, и передайте соответствующий тайм-аут. Например:

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
}

Ответ 2

Вы можете попытаться захватить сигнал, чтобы остановить операцию чтения. используйте сигнал тревоги (1) перед чтением, и если функция чтения не была возвращена, сигнал будет посылать сигнал SIGALRM, тогда вы можете создать функцию обработки сигнала, чтобы зафиксировать этот сигнал, например:

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

Но использование select или poll или epoll будет лучше, если ваша программа большая.

Ответ 3

Что такое VMIN и VTIME для?

Если MIN > 0 и TIME = 0, MIN устанавливает количество символов для приема до того, как чтение будет выполнено. Поскольку TIME равно нулю, таймер не используется.

Если MIN = 0 и TIME > 0, TIME используется как значение тайм-аута. Чтение будет выполняются, если считывается один символ или превышено время (t = ВРЕМЯ * 0,1 с). Если TIME превышено, символ не будет возвращен.

Если MIN > 0 и TIME > 0, TIME служит в качестве межсимвольного таймера. чтение будет выполнено, если будут получены MIN символов или время между двумя символами превышает TIME. Таймер перезапускается каждый раз символ принимается и становится активным только после первого символ получен.

Если MIN = 0 и TIME = 0, чтение будет выполнено немедленно. количество доступных в настоящее время символов или количество символов запрос будет возвращен. Согласно Антонино (см. Вклады) вы можете выдать fcntl (fd, F_SETFL, FNDELAY); перед чтением, чтобы получить тот же результат.

Источник: http://tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html

Ответ 4

select() - это способ решить эту проблему.

В Интернете есть несколько страниц, в которых рассказывается, как использовать select(), например http://www.unixguide.net/unix/programming/2.1.1.shtml

Ответ 5

Существует несколько возможных подходов. Если программа в конечном итоге будет синхронизироваться более чем с одной операцией ввода-вывода, select() - это четкий выбор.

Однако, если единственный вход из этого ввода/вывода, то выбор неблокирующего ввода-вывода и синхронизации является простым методом. Я расширил его от односимвольного ввода/вывода до многосимвольного, чтобы сделать его более общепринятым примером:

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