首页 文章

从串口读取时如何实现read()的超时(C / C)

提问于
浏览
13

我正在使用文件描述符和posix / unix read()函数从C中的串行端口读取字节 . 在这个例子中,我从串口读取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 / ttyS0的设备未发送任何信息,程序将挂起 . 如何设置超时?

我试过像这样设置一个时间:

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秒超时?

任何帮助表示赞赏 . 提前致谢 :-)

5 回答

  • 1

    什么是VMIN和VTIME用于?

    如果MIN> 0且TIME = 0,则MIN设置在读取满足之前接收的字符数 . 当TIME为零时,不使用定时器 . 如果MIN = 0且TIME> 0,则TIME用作超时值 . 如果读取单个字符或超过TIME(t = TIME * 0.1 s),则读取将被满足 . 如果超过TIME,则不返回任何字符 . 如果MIN> 0且TIME> 0,则TIME用作字符间计时器 . 如果接收到MIN字符,或者两个字符之间的时间超过TIME,则将满足读取 . 每次收到一个字符时,计时器都会重新启动,并且只有在收到第一个字符后才会激活 . 如果MIN = 0且TIME = 0,则立即满足读取 . 将返回当前可用的字符数或请求的字符数 . 根据Antonino(见贡献),你可以发出一个fcntl(fd,F_SETFL,FNDELAY);在阅读之前得到相同的结果 .

    资料来源:http://tldp.org/HOWTO/Serial-Programming-HOWTO/x115.html

  • 0

    是的,请使用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
    }
    
  • 0

    您可以尝试捕获信号以停止读取操作 . 在读取之前使用alarm(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会更好 .

  • 2

    select()是解决这个问题的方法 .

    互联网上有几个页面将提供有关如何使用select()的信息,例如http://www.unixguide.net/unix/programming/2.1.1.shtml

  • 17

    有几种可能的方法 . 如果程序最终将定时多个i / o操作, select() 是明智的选择 .

    但是,如果唯一的输入来自此i / o,则选择非阻塞i / o和时序是一种简单的方法 . 我已经将它从单字符i / o扩展到多字符,使其成为一个更普遍的完整示例:

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

相关问题