日期:2014-05-16  浏览次数:20893 次

关于linux中select 方法延迟问题-----当我while select 后

笔者在写程序的时候,发现了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方法原来还有这样用,我也是刚刚发现,和大家分享!