笔者在写程序的时候,发现了select方法真的是非常奇妙,当我们读某一个串口数据,并且要有超时机制的时候,用select方法就可以时间,或者也可以用time(NULL)来计时实现,我们今天只探讨用select方法。
我们来看看程序:
/*-----------------------------------------------------------------------------
函数名: serial_read
参数: int fd,char *str,unsigned int len,unsigned int timeout
返回值: 在规定的时间内读取数据,超时则退出,超时时间为ms级别
描述: 向fd描述符的串口接收数据,长度为len,存入str,timeout 为超时时间
*-----------------------------------------------------------------------------*/
int serial_read(int fd, char *str, unsigned int len, unsigned int timeout)
{
fd_set rfds;
struct timeval tv;
int ret; //每次读的结果
int sret; //select监控结果
int readlen = 0; //实际读到的字节数
char * ptr;
ptr = str; //读指针,每次移动,因为实际读出的长度和传入参数可能存在差异
FD_ZERO(&rfds); //清除文件描述符集合
FD_SET(fd,&rfds); //将fd加入fds文件描述符,以待下面用select方法监听
/*传入的timeout是ms级别的单位,这里需要转换为struct timeval 结构的*/
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout%1000)*1000;
/*防止读数据长度超过缓冲区*/
//if(sizeof(&str) < len)
// len = sizeof(str);
/*开始读*/
while(readlen < len)
{
sret = select(fd+1,&rfds,NULL,NULL,&tv); //检测串口是否可读
if(sret == -1) //检测失败
{
perror("select:");
break;
}
else if(sret > 0) //检测成功可读
{
ret = read(fd,ptr,1);
printf("sec: %d,usec: %d\n",tv.tv_sec,tv.tv_usec);
if(ret < 0)
{
perror("read err:");
break;
}
else if(ret == 0)
break;
readlen += ret; //更新读的长度
ptr += ret; //更新读的位置
}
else //超时
{
printf("timeout!\n");
break;
}
}
return readlen;
}
一目了然,计时从fd这个设备描述符读取数据,读入的数据存入str中,长度为len,超时时间为timeout。
这里大家一定会很奇怪,为什么我要一个个字节的读出数据?这个函数原来也不是笔者写的,是参照一个大牛的写法,我一开始也没明白,后来终于明白:因为要超时!
tv这个参数,会在记录调用了select后消耗的时间,这样当我每次while循环的时候,就可以检测并且记录tv还剩多少时间能让我消耗,若tv的值为0了,那么就是超时了。
打开串口,我们来看看运行结果:
可以看到我从串口输入了11个数据,每传入一个字节的数据大概耗时70us
(笔者输入的是i love you ,但是原先输入了多次i love you linux and unix and meego缓冲区没有清除,35个字节太长没有办法截图,谅解~~~)
如此就可以实现超时读数据,select方法原来还有这样用,我也是刚刚发现,和大家分享!