Linux下I/O模式---IO多路复用(select)1

文章详细介绍了如何使用select函数实现I/O多路复用,以一个简易服务器的示例来阐述了其工作原理。通过创建文件描述符集合,调用select函数阻塞等待,当有文件描述符准备就绪时进行处理。select的限制在于最大监听文件描述符数量及效率问题。
摘要由CSDN通过智能技术生成

括号内都是个人理解

使用环境:一个进程同时处理多路输入输出流时,比如 搭建服务器

特点:让每一个输入输出流及时得到响应,使cpu不在一个输入输出流处阻塞

个人理解:让输入输出流“排好队”等着cpu处理,而不是让cpu等他们。

构建I/O多路复用的基本思路

1.先构造一张有关文件描述符的表(集合,数组)(类似银行大厅)
2.将需要用的文件描述符添加到这个表中;(先坐着歇一歇)
3.调用相应的函数(select、poll、epoll)阻塞
4.当这些文件描述符中有一个准备好I/O操作(排好队)了,函数就返回(停止阻塞)
5.判断是哪一个或哪些文件描述符产生了I/O操作(要办的事)
6.CPU做出对应的逻辑处理(办事,办事有时候还需要“本人”配合)

实现IO多路复用的方式
1.select实现

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
   功能:select用于监测是哪个或哪些文件描述符产生事件;
         有一个或多个同时产生时间返回值。
   参数:nfds:    监测的最大文件描述个数
        (这里是个数,使用的时候注意,与文件中最后一次打开的文件
          描述符所对应的值的关系是什么?)
     readfds:  读事件集合; //读(用的多)
     writefds: 写事件集合;  //NULL表示不关心
     exceptfds:异常事件集合;  
     timeout:超时检测 1
   如果不做超时检测:传 NULL 
   select返回值:  <0 出错
               >0 表示有事件产生;
   如果设置了超时检测时间:&tv
      select返回值:
         <0 出错
        >0 表示有事件产生;
        ==0 表示超时时间已到;

     struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };
           
 void FD_CLR(int fd, fd_set *set);//将fd从表中清除
 int  FD_ISSET(int fd, fd_set *set);//判断fd是否在表中
 void FD_SET(int fd, fd_set *set);//将fd添加到表中
 void FD_ZERO(fd_set *set);//清空表
 

实例(核心代码)简易服务器

	//I/O并发准备工作
    fd_set sockfds, tempfds;

    FD_ZERO(&sockfds);
    FD_SET(sockfd, &sockfds);  //把文件描述符放进表里
    int maxfd = sockfd;		//定义最大的描述符用来计数(做标志)
    char buf[128];
    int ret, i, recvbyte,maxfdtemp;

    while (1)
    {
        tempfds = sockfds;
        //阻塞等待描述符事件
        ret = select(maxfd + 1, &tempfds, NULL, NULL, NULL);
        if (ret < 0)		//日常一判
        {
            perror("select err");
            return -1;
        }

        maxfdtemp = maxfd;    //临时用来便利的,防止后面改maxfd出错(我不知道有没有出错的可能)
        for (i = 0; i <= maxfdtemp; i++)	//i代表描述符
        {
            if (FD_ISSET(i, &tempfds))    //判断描述符是否在表内,在就进来
            {
                if (i == sockfd)  //判断是不是等待链接的描述符
                {
                
                		//建立与客户端的链接,接收文件描述符与客户端ip信息
                    acceptfd = accept(sockfd, (struct sockaddr *)&caddr, &len);  
                    if (acceptfd < 0)		//日常一判
                    {
                        perror("accept err");
                        return -1;
                    }
                    FD_SET(acceptfd, &sockfds);   //把新的描述符放进表里
                    if (acceptfd > maxfd)    //如果它是最大的描述符,那就把它跟maxfd换一换
                        maxfd = acceptfd;

                    printf("用户 %d 已登录:ip:%s ,port:%d\n",
                           acceptfd, inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));  //将客户端信息打印
                }
                else//只剩下新建的客户端的链接了,没写发数据的代码
                {
                    recvbyte = recv(i, buf, sizeof(buf), 0);  //接收客户端描述符i的信息,recvbyte用来判断描述符状态
                    if (recvbyte < 0)//日常一判
                    {
                        perror("recv err");
                        return -1;
                    }
                    else if (recvbyte == 0)   //退出
                    {
                        printf("用户%d 已退出\n", i);
                        FD_CLR(i, &sockfds);    //先把它从表里删除
                        close(i);        //再关闭描述符
                        if (i == maxfd)   //如果它是最大的,那么就把maxfd这个标志给第二大的
                        {
                            for (int j = i; j > 0; j--)     
                                if ((FD_ISSET(j, &sockfds)))    //从j往下一个一个遍历,如果在表里就给它
                                    maxfd = j;
                        }
                    }
                    else
                    {
                        printf("用户%d: %s \n", i, buf);   //输出客户端发的信息
                    }
                }
            }
        }
    }

总结select实现IO多路复用特点

  1. 一个进程最多只能监听1024个文件描述符 (千级别)(在不改底层的情况下)
  2. select被唤醒之后需要重新轮询一遍驱动的poll函数,效率比较低(消耗CPU资源);
  3. select每次会清空表,每次都需要拷贝用户空间的表到内核空间,效率低(一个进程04G,03G是用户态,3G~4G是内核态,拷贝是非常耗时的);
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值