linux 下多路复用技术 之 select

在linux终端中输入 man 2 select  命令 就可以查看select函数的具体应用方法

 select函数的不足:内核必须检查多余的文件描述符,每次调用select()之后必须重置被监听的文件描述符集合,而且可监听的文件描述符的个数受限制,相比较poll函数对于监听具有更好的应用价值。

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <string.h>

#define MAX_BUFFER_SIZE		1024			/* 缓冲区大小*/
#define IN_FILES		3			/* 多路复用输入文件数目*/
#define TIME_DELAY		60			/* 超时时间秒数 */
#define MAX(a, b)		((a > b)?(a):(b))

int main(void)
{
	int fds[IN_FILES]; //文件描述符数组集合
	char buf[MAX_BUFFER_SIZE]; //从管道中读取数据 数组集合
	int i, res, real_read, maxfd;
	struct timeval tv; //select 延时时间结构体  分为秒和毫秒
	fd_set inset,tmp_inset; // inset作为监控描述符号

	/*首先按一定的权限打开两个源文件*/
	fds[0] = 0; //这个表示的时当前的监控终端

	if((fds[1] = open ("in1", O_RDONLY|O_NONBLOCK)) < 0) // 打开第一个管道
	{
		printf("Open in1 error\n");
		return 1;
	}

 	if((fds[2] = open ("in2", O_RDONLY|O_NONBLOCK)) < 0)//打开第二个管道
 	{
 		printf("Open in2 error\n");
		return 1;
	}

	/*取出两个文件描述符中的较大者*/
  	maxfd = MAX(MAX(fds[0], fds[1]), fds[2]); //在三个描述符中选择最大的描述符 作为select函数的第一个参数


  	/*初始化读集合inset,并在读集合中加入相应的描述集*/
  	FD_ZERO(&inset);//清空描述集
  	for (i = 0; i < IN_FILES; i++)
  	{
  		FD_SET(fds[i], &inset);
  	}//将描述符家加入书组
  	FD_SET(0, &inset);//将当前运行程序的的终端也加入描述符集合

  	tv.tv_sec = TIME_DELAY;//延时时间秒
  	tv.tv_usec = 0;//延时时间毫秒

  	/*循环测试该文件描述符是否准备就绪,并调用select函数对相关文件描述符做对应操作*/
  	while(FD_ISSET(fds[0],&inset) || FD_ISSET(fds[1],&inset) || FD_ISSET(fds[2], &inset)) //如何任何一个描述符为inset集合中的一个元素的时候,都会返回非零值
  	{
  		tmp_inset = inset;//这个时因为经过select函数后,inset集合中的描述符号都会被改变,所以用tmp_inset作为中间变量
  		res = select(maxfd + 1, &tmp_inset, NULL, NULL, &tv);
  		//注意第一个参数是为需要监视的文件描述符中的最大值+1,第三个NULL表示没有需要监视的写描述符集合,第四个NULL表示没有需要监视的异常处理文件描述服

  		switch(res) //通过不同的返回值就可以进行相应的处理
  		{
  			case -1://出错
  			{
  				printf("Select error\n");
  				return 1;
  			}
  			break;

  			case 0: /* Timeout */
  			{
  				printf("Time out\n");
  				return 1;
  			}
  			break;

  			default:
  			{
  				for (i = 0; i < IN_FILES; i++) //轮训三个描述符
  				{
  					if (FD_ISSET(fds[i], &tmp_inset))//在调用select之后,可以用来检测文件描述符集中的文件描述符是否发生变化,如果发生变化将返回true
		  			{
		  				memset(buf, 0, MAX_BUFFER_SIZE);//清空
		  				real_read = read(fds[i], buf, MAX_BUFFER_SIZE);//读取

		  				if (real_read < 0)
		  				{
		  					if (errno != EAGAIN)
		  					{
		  						return 1;
		  					}
		  				}
		  				else if (!real_read)//如果读取失败
		  				{
		  					close(fds[i]);//关闭这个文件
		  					FD_CLR(fds[i], &inset);//将这个文件描述符从这个集合中清除
		  				}
		  				else
		  				{
		  					if (i == 0)
		  					{
		  						if ((buf[0] == 'q') || (buf[0] == 'Q'))//如果在当前运行程序的终端中输入Q或q将会退出程序的运行
		  						{
		  							return 1;
		  						}
		  					}
		  					else
		  					{
		  						buf[real_read] = '\0';//在数组后面加上结束符描述
		  						printf("%s", buf);//打印
		  					}
		  				}
		  			} /* end of if */
  				} /* end of for */
  			}
  			break;

  		} /* end of switch */
  	} /*end of while */

  	exit(0);
}

验证方法: 1】首先一定要在同一个工作目录下

                  2】然后分别在当前工作目录中打开两个终端创建两个管道文件 

                        在第一个终端1中 两条命令(1)mknod  in1 p   (2)cat>in1   

                        在第二个终端2中 两条命令(1)mknod in2 p     (2)cat>in2

                   3】在当前目录中打开第三个终端3运行上面程序的可执行文件

                   4】在终端1或者终端2中随意输入字符,观察终端3的变化情况

                   5】在终端3中输入Q或q 将会退出




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值