首先搞清楚select的参数、返回值,代表些什么。
1、头文件 #include <sys/select.h>
2、参数 函数原型:int select (int maxfd + 1,fd_set *readset,fd_set *writeset,fd_set *exceptset,const struct timeval * timeout);
参数一:最大的文件描述符加1。
参数二:用于检查可读性,
参数三:用于检查可写性,
参数四:用于检查带外数据,
参数五:一个指向timeval结构的指针,用于决定select等待I/o的最长时间。如果为空将一直等待。timeval结构的定义:struct timeval{
>0:就绪描述字的正数目
我们来看看程序:
- /*-----------------------------------------------------------------------------
- 函数名: 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;
- }
这里大家一定会很奇怪,为什么我要一个个字节的读出数据?这个函数原来也不是笔者写的,是参照一个大牛的写法,我一开始也没明白,后来终于明白:因为要超时!
tv这个参数,会在记录调用了select后消耗的时间,这样当我每次while循环的时候,就可以检测并且记录tv还剩多少时间能让我消耗,若tv的值为0了,那么就是超时了。
打开串口,我们来看看运行结果:
可以看到我从串口输入了11个数据,每传入一个字节的数据大概耗时70us
(笔者输入的是i love you ,但是原先输入了多次i love you linux and unix and meego缓冲区没有清除,35个字节太长没有办法截图,谅解~~~)
如此就可以实现超时读数据,select方法原来还有这样用,我也是刚刚发现,和大家分享!