我正在使用文件描述符和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秒超时?
感谢任何帮助。在此先感谢: - )
答案 0 :(得分:18)
是的,请使用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
}
答案 1 :(得分:3)
VMIN和VTIME用于什么?
如果MIN> 0和TIME = 0,MIN设置要接收的字符数 在阅读满意之前。当TIME为零时,不使用计时器。
如果MIN = 0且TIME&gt; 0,TIME用作超时值。阅读会 如果读取单个字符或超过TIME,则表示满意(t = 时间* 0.1秒)。如果超过TIME,则不会返回任何字符。
如果MIN> 0和TIME&gt; 0,TIME用作字符间计时器。该 如果收到MIN字符或时间,则读取将被满足 两个字符之间超过TIME。每次都重启计时器 收到一个字符,并且只在第一个字符后变为活动状态 已收到角色。
如果MIN = 0且TIME = 0,则立即满足读取。该 当前可用的字符数或字符数 请求将被退回。根据安东尼诺(见贡献), 你可以发出一个fcntl(fd,F_SETFL,FNDELAY);在阅读之前得到 同样的结果。
答案 2 :(得分:1)
您可以尝试捕获信号以停止读取操作。在读取之前使用报警(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 :(得分:0)
select()是我解决这个问题的方法。
互联网上有几个页面会提供有关如何使用select()的信息,例如http://www.unixguide.net/unix/programming/2.1.1.shtml
答案 4 :(得分:0)
有几种可能的方法。如果程序最终将定时多个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;
}