有意思的select~

前言

最近在写一个小程序,也就是简单的系统调用,但是神奇的是,我用的这个系统调用刚好就阻塞了。如果你也写过应用程序,肯定也会遇到过这样的问题。

后来,发现了select这个好东西,可以用来监听文件描述。

select的作用

如果我们在read一个文件,如果文件马上有东西返回,那是非常愉快的事情,但是经常遇到一些情况,read不能马上返回数据,这时候,会造成我们的线程阻塞,就卡在那里不动。如果是ui界面,那情况就显得很尴尬,你的ui卡主了,作为一个计算机用户,那是一件非常崩溃的事情的。

人们为了解决这个问题,select就出现了。

select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation (e.g., input possible). A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g., read(2)) without blocking.

select 和 pselect 允许程序监听文件描述符,文件描述符是打开文件的时候返回从一个整数,这个整数代表了一个文件,大家都叫他做文件描述符。直到文件描述符准备好了IO操作。

原来的代码

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/ioctl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>

#define RETRY_TIMES (20)
#define COM_STR "ezsp ver"

int main(int argc, char * const argv[])
{
	int fd_in,fd_out,size;
	int retry_times=0;
	int ret=0;
	
	//char s[ ]="info\n",buffer[1024];
	char s[ ]="version\n",buffer[1024];
	
	printf("=== weiqifa ===Zigbee test start ...\n");
	printf("argc:%d\n",argc);
	printf("argv[0]:%s\n",argv[0]);

	/*打开写管道文件*/
	fd_in=open("/dev/gateway_in",O_RDWR);
	if(fd_in<0){
		printf("===weiqifa=== open error:%d\n",fd_in);
		return(0);
	}
	
	/*打开读管道文件*/
	fd_out=open("/dev/gateway_out",O_RDWR);
	if(fd_out<0){
		printf("===weiqifa=== open error:%d\n",fd_out);
		return(0);
	}


	/*循环读写*/
	do
	{
		ret = write(fd_in,s,sizeof(s));
		size= read(fd_out,buffer,sizeof(buffer));
		printf("%s",buffer);

		if(strncmp(COM_STR,buffer,strlen(COM_STR) -1) == 0){
			break;
		}
		
		usleep(3000);
	}while(retry_times++ <=RETRY_TIMES);

	if(retry_times>= RETRY_TIMES)
	{
		printf("\nfail\n");
		return (-1);
	}

	/*关闭管道*/
	close(fd_out);
	close(fd_in);
	printf("\nsuccess\n");
	return (0);
}

修改后的代码

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/ioctl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>

#define RETRY_TIMES (20)
#define COM_STR "ezsp ver"

int main(int argc, char * const argv[])
{
	int fd_in,fd_out,size;
	int retry_times=0;
	int ret=0;
	struct timeval tv;
	fd_set rdfds;

	/*清空rdfds*/
	FD_ZERO(&rdfds);
	
	//char s[ ]="info\n",buffer[1024];
	char s[ ]="version\n",buffer[1024];
	
	printf("=== weiqifa ===Zigbee test start ...\n");
	printf("argc:%d\n",argc);
	printf("argv[0]:%s\n",argv[0]);

	/*打开写管道文件*/
	fd_in=open("/dev/gateway_in",O_RDWR);
	if(fd_in<0){
		printf("===weiqifa=== open error:%d\n",fd_in);
		return(0);
	}
	
	/*打开读管道文件*/
	fd_out=open("/dev/gateway_out",O_RDWR);
	if(fd_out<0){
		printf("===weiqifa=== open error:%d\n",fd_out);
		return(0);
	}


	/*循环读写*/
	do
	{
		ret = write(fd_in,s,sizeof(s));
		tv.tv_sec = 1; /*秒*/
		tv.tv_usec = 500; /*微秒*/

		/*添加监听的设备描述符*/
		FD_ZERO(&rdfds);
		FD_SET(fd_out,&rdfds);
		/*监听fd_out*/
		ret = select(fd_out+1,&rdfds,NULL,NULL,&tv);
		if(ret<0){
			printf("selcet error\r\n");
			retry_times = RETRY_TIMES;
			break;
		}else if(ret == 0){ /*超时*/
			printf("timeout \r\n");
			retry_times = RETRY_TIMES;
			break;
		}else{
			printf("ret = %d \r\n",ret);
		}
		size= read(fd_out,buffer,sizeof(buffer));
		printf("%s",buffer);

		if(strncmp(COM_STR,buffer,strlen(COM_STR) -1) == 0){
			break;
		}
		
		usleep(3000);
	}while(retry_times++ <=RETRY_TIMES);

	if(retry_times>= RETRY_TIMES)
	{
		printf("\nfail\n");
		return (-1);
	}

	/*关闭管道*/
	close(fd_out);
	close(fd_in);
	printf("\nsuccess\n");
	return (0);
}

select代码的小例子

       #include <stdio.h>
       #include <stdlib.h>
       #include <sys/select.h>

       int
       main(void)
       {
           fd_set rfds;
           struct timeval tv;
           int retval;

           /* Watch stdin (fd 0) to see when it has input. */

           FD_ZERO(&rfds);
           FD_SET(0, &rfds);

           /* Wait up to five seconds. */

           tv.tv_sec = 5;
           tv.tv_usec = 0;

           retval = select(1, &rfds, NULL, NULL, &tv);
           /* Don't rely on the value of tv now! */

           if (retval == -1)
               perror("select()");
           else if (retval)
               printf("Data is available now.\n");
               /* FD_ISSET(0, &rfds) will be true. */
           else
               printf("No data within five seconds.\n");

           exit(EXIT_SUCCESS);
       }

执行 第一次执行的时候,我没有输入任何内容,这时候,select就一直监听标准输入,因为没有输入就一直等,等到了超时时间,程序就退出了。

第二次执行的时候,我给标准输入输入东西了,select马上就返回,并打印了数据是有效的。

深入理解select模型

理解select模型的关键在于理解fd_set,为说明方便,取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。

  • 执行fd_set set; FD_ZERO(&set); 则set用位表示是0000,0000。

  • 若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)

  • 若再加入fd=2,fd=1,则set变为0001,0011

  • 执行select(6,&set,0,0,0)阻塞等待

  • 若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

最后举个例子

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <linux/ioctl.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>

int main(int argc, char * const argv[])
{
	int fd_out,size;
	int ret=0;
	struct timeval tv;
	fd_set rdfds;
	char buffer[1024];

	printf("=== weiqifa === test start ...\n");
	printf("argc:%d\n",argc);
	printf("argv[0]:%s\n",argv[0]);

	/*打开读管道文件*/
	fd_out=open("./test",O_RDWR);
	if(fd_out<0){
		printf("===weiqifa=== open error:%d\n",fd_out);
		return(0);
	}

	/*清空rdfds*/
	FD_ZERO(&rdfds);
	/*添加监听的设备描述符*/
	FD_SET(fd_out,&rdfds);
	tv.tv_sec = 10; /*秒*/
	tv.tv_usec = 500; /*微秒*/
	/*监听fd_out*/
	ret = select(fd_out+1,&rdfds,NULL,NULL,&tv);
	if(ret<0){
		printf("selcet error\r\n");
		goto exit;
	}else if(ret == 0){ /*超时*/
		printf("timeout1 \r\n");
		goto exit;
	}else{
		printf("ret = %d \r\n",ret);
	}
	size= read(fd_out,buffer,sizeof(buffer));
	printf("%s",buffer);

exit:
	/*关闭管道*/
	close(fd_out);
	printf("\nsuccess\n");
	return (0);
}

执行截图

===========

  

PS想加入技术群的同学,加了我好友后,就给我发「篮球的大肚子」这句话,有可能机器人打瞌睡,可以多发几次,不要发与技术无关的消息或者推广。

如果想获取学习资料,就在公众号后台回复「1024」,足够多的学习资料可以让你学习。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值