Linux-select剖析

Linux高级IO ----- select 阅读详情

一、select函数

select函数是IO多路复用的函数,它主要的功能是用来等文件描述符中的事件是否就绪,select可以使我们在同时等待多个文件缓冲区 ,减少IO等待的时间,能够提高进程的IO效率。

select()函数允许程序监视多个文件描述符,等待所监视的一个或者多个文件描述符变为“准备好”的状态。所谓的”准备好“状态是指:文件描述符不再是阻塞状态,可以用于某类IO操作了,包括可读,可写,发生异常三种

二、参数介绍

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
ndfs

等待的文件描述符的最大值+1,例如:应用进程想要去等待文件描述符3,5,8的事件,则

nfds=max(3,5,8)+1;

fd_set类型

readfds和writefds,exceptfds的类型都是fd_set,那么fd_set类型是什么呢?

  • fd_set类型本质是一个位图,位图的位置 表示 相对应的文件描述符,内容表示该文件描述符是否有效,1代表该位置的文件描述符有效,0则表示该位置的文件描述符无效。
  • 如果将文件描述符2,3设置位图当中,则位图表示的是为1100。
  • fd_set的上限是1024个文件描述符。
readfds
  • readfds是 等待读事件的文件描述符集合,.如果不关心读事件(缓冲区有数据),则可以传NULL值。
  • 应用进程和内核都可以设置readfds,应用进程设置readfds是为了通知内核去等待readfds中的文件描述符的读事件.而 内核设置readfds是为了告诉应用进程哪些读事件生效

writefds

与readfds类似,writefds是等待写事件(缓冲区中是否有空间)的集合,如果不关心写事件,则可以传值NULL。

exceptfds

如果内核等待相应的文件描述符发生异常,则将失败的文件描述符设置进exceptfds中,如果不关心错误事件,可以传值NULL。

timeout

设置select在内核中阻塞的时间,如果想要设置为非阻塞,则设置为NULL。如果想让select阻塞5秒,则将创建一个struct timeval time={5,0};

其中struct timeval的结构体类型是:

           struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };
返回值
  • 如果没有文件描述符就绪就返回0;
  • 如果调用失败返回-1;
  • 如果timeout中中readfds中有事件发生,则返回timeout剩下的时间

3.select的工作流程

应用进程和内核都需要从readfds和writefds获取信息,其中,内核需要从readfds和writefds知道哪些文件描述符需要等待,应用进程需要从readfds和writefds中知道哪些文件描述符的事件就绪.

4.Select服务器

如果是一个select服务器进程,则服务器进程会不断的接收有新链接,每个链接对应一个文件描述符,如果想要我们的服务器能够同时等待多个链接的数据的到来,我们监听套接字listen_sock读取新链接的时候,我们需要将新链接的文件描述符保存到read_arrys数组中,下次轮询检测的就会将新链接的文件描述符设置进readfds中,如果有链接关闭,则将相对应的文件描述符从read_arrys数组中拿走。

一张图看懂select服务器:

简易版的select服务器:

server.hpp文件:

#pragma once                                                                                                           
  #include<iostream>    
  #include<sys/socket.h>    
  #include<sys/types.h>    
  #include<netinet/in.h>    
  #include<string.h>    
  using std::cout;    
  using std::endl;    
  #define BACKLOG 5    
      
  namespace sjp{    
    class server{    
      public:    
      static int Socket(){    
        int sock=socket(AF_INET,SOCK_STREAM,0);    
        if(sock>0)    
        return sock;    
        if(sock<0)    
          exit(-1);    
W>    }    
      
      static bool Bind(int sockfd,short int port){    
        struct sockaddr_in lock;    
        memset(&lock,'\0',sizeof(lock));    
        lock.sin_family=AF_INET;    
        lock.sin_port=htons(port);    
        lock.sin_addr.s_addr=INADDR_ANY;    
        if(bind(sockfd,(struct sockaddr*)&lock,(socklen_t)sizeof(lock))<0){    
                  exit(-2);    
        }    
        return true;    
      }    
 
     static bool Listen(int sockfd){
        if(listen(sockfd,BACKLOG)<0){
          exit(-3);
        }
        return true;
      }
    };
  }

select_server.hpp文件

#pragma once                                                                                                           
  #include<vector>
  #include"server.hpp"
  #include<unistd.h>
  #include<time.h>
  
  namespace Select{
    class select_server{
      private:
        int listen_sock;//监听套接字    
        int port;    
          
      public:    
        select_server(int _port):port(_port){}    
      
        //初始化select_server服务器    
        void InitServer(){    
          listen_sock=sjp::server::Socket();    
          sjp::server::Bind(listen_sock,port);    
          sjp::server::Listen(listen_sock);    
        }    
      
      
        void Run(){    
          std::vector<int> readfds_arry(1024,-1);//readfds_arry保存读事件的文件描述符    
          readfds_arry[0]=listen_sock;//将监听套接字保存进readfds_arry数组中    
          fd_set readfds;    
          while(1){    
          FD_ZERO(&readfds);    
          int nfds=0;    
          //将read_arry数组中的文件描述符设置进程readfds_arry位图中    
          for(int i=0;i<1024;i++)    
          {    
            if(readfds_arry[i]!=-1){    
            FD_SET(readfds_arry[i],&readfds);    
           if(nfds<readfds_arry[i]){
              nfds=readfds_arry[i];
            }
            }
          }
  
          //调用select对readfds中的文件描述符进行等待数据
          switch(select(nfds+1,&readfds,NULL,NULL,NULL)){
            case 0:
              //没有一个文件描述符的读事件就绪
              cout<<"select timeout"<<endl;
              break;
            case -1:
              //select失败
              cout<<"select error"<<endl;
            default:
              {
              //有读事件发生
                Soluation(readfds_arry,readfds);
                break;
              }
          }           
          }
        }
                                                                                                                         
        void Soluation(std::vector<int>& readfds_arry,fd_set readfds){
W>        for(int i=0;i<readfds_arry.size();i++){
            if(FD_ISSET(readfds_arry[i],&readfds))
            {
              if(readfds_arry[i]==listen_sock){
                //有新链接到来
                struct sockaddr peer;
                socklen_t len;                                                                                           
                int newfd=accept(listen_sock,&peer,&len);
                cout<<newfd<<endl;
                //将新链接设置进readfds_arry数组中
                AddfdsArry(readfds_arry,newfd);
              }
              else{
                //其他事件就绪
                char str[1024];
                int sz=recv(readfds_arry[i],&str,sizeof(str),MSG_DONTWAIT);
                switch(sz){
                  case -1:
                    //读取失败
                    cout<<readfds_arry[i]<<": recv error"<<endl;
                    break;
                  case 0:
                    //对端关闭
                    readfds_arry[i]=-1;
                    cout<<"peer close"<<endl;
                    break;
                  default:
                    str[sz]='\0';
                    cout<<str<<endl;
                    break;
                }
              }
            }
          }
        }
 
        void AddfdsArry(std::vector<int>& fds_arry,int fd){
W>        for(int i=0;i<fds_arry.size();i++){
            if(fds_arry[i]==-1){
              fds_arry[i]=fd;
              break;
            }
          }
        }
    };
  }       

select_server.cc文件

#include"select_server.hpp"    
    
int main(int argv,char* argc[]){    
  if(argv!=2){    
    cout<<"./selectserver port"<<endl;    
    exit(-4);    
  }    
                                                                                                                         
  int port=atoi(argc[1]);//端口号
  Select::select_server* sl=new Select::select_server(port);    
  sl->InitServer();                                             
  sl->Run();                                                                                                   
}        

五.Select的缺陷

  • 由于fd_set的上限是1024,所以select能等待的读事件的文件描述符和写事件的文件描述是有上限的,如果作为一个大型服务器,能够同时链接的客户端是远远不够的。
  • 每次应用进程调用一次select之前,都需要重新设定writefds和readfds,如果进行轮询调用select,这对影响cpu效率。
  • 内核每一次等待文件描述符 都会重新扫描所有readfds或者writefds中的所有文件描述符,如果有较多的文件描述符,则会影响效率。
select函数详解 select函数详解背景说明定义介绍、参数说明原理返回值pselect总结案例案例1案例2 说明:本文整合网络资源和man帮助文档,请酌情参考。 背景 select函数是实现IO多路复用的一种方式。 什么是IO多路复用? 举一个简单地网络服务器的例子,如果你的服务器需要和多个客户端保持连接,处理客户端的请求,属于多进程的并发问题,如果创建很多个进程来处理这些IO流,会导致CPU占有率很高。所以人... 阅读详情

相关推荐

Linux select内核源码剖析

poll Linux内核源码剖析 select Linux内核源码剖析 select Linux内核源码剖析 select的原理其实是和poll是一样的,都是采用轮询的方式。select相对于poll也许是比较节省空间吧,因为select是采用bitmap来标志的 本文先讲解一下如何在应用层使用select,然后再深入内核剖析select机制 select应用程序 select可以监听多个文件描述...

JT同学的博客 4378

【Linux】——select详解

select函数是IO多路复用的函数,它主要的功能是用来等文件描述符中的事件是否就绪,select可以使我们在同时等待多个文件缓冲区 ,减少IO等待的时间,能够提高进程的IO效率。

sjp11的博客 2万+

linux 内核poll/select/epoll实现剖析

f_ops.poll和wait_queue poll/select/epoll的实现都是基于文件提供的poll方法(f_op-&gt;poll),该方法利用poll_table提供的_qproc方法向文件内部事件掩码_key对应的的一个或多个等待队列(wait_queue_head_t)上添加包含唤醒函数(wait_queue_t.func)的节点(wait_queue_t),并检查文件当前就绪...

在思考的路上 1447

linux select源码,Linux select/poll源码剖析

Linux select/poll源码剖析linux内核版本:2.6.34在读select、poll源码前,需要先了解的知识点:等待队列文件系统(主要是进程的打开文件描述符表以及struct file)poll机制资源注册监听poll() -> poll_wait(struct file *, wait_queue_t *, poll_table *pt) -> pt->qpro...

weixin_42172204的博客 174

Linux select/poll/epoll 实现原理剖析

select(2),poll(2) 以轮询的方式检查 fd 集合,轮询一遍没有就绪事件发生就会将自己挂起,等待监视的文件将自己唤醒(超时和信号都可以唤醒)。而 epoll(7) 是设备就绪时,调用回调函数,把就绪 fd 放入就绪链表中,并唤醒在 epoll_wait(2) 中进入睡眠的进程。虽然都要睡眠和交替,但是 select(2) 和 poll(2) 在“醒着”的时候要遍历整个 fd 集合,而 epoll(7) 在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的 CPU 时间。

u014441159的博客 692

linux系统中select函数的用法实现

select底层通过轮询方式获取读,写,异常位图中注册的socket文件事件,如果检测到有socket文件处于就绪状态,则会将socket对应的事件设置到输出位图,等所有位图中的socket都被轮询完,会统一将输出位图通过copy_to_user函数复制到输入位图,并且覆盖掉输入位图注册信息。,select总共有三种位图,分别为读,写,异常位图,用户程序预先将socket文件描述符注册至读,写,异常位图,然后通过select系统调用轮询位图中的socket的读,写,异常事件。//微妙,超时多少微秒。

嵌入式技术开发 1558

Linux 下 select 函数剖析

在Linux中,我们可以使用 select 函数实现I/O端口的复用,传递给 select 函数的参数会告诉内核: • 我们所关心的文件描述符 • 对每个描述符,我们所关心的状态。(我们是要想从一个文件描述符中读或者写,还是关注一个描述符中是否出现异常) • 我们要等待多长时间。(我们可以等待无限长的时间,等待固定的一段时间,或者根本就不等待) ...

Zehpyr的博客 346

Linux I/O 多路复用实战:深入剖析 Select 与 Poll

`FD_ISSET(int fd, fd_set *set)`:检查文件描述符 `fd` 是否在集合 `set` 中,通常用于 `select` 返回后判断哪个描述符就绪。2. **更精细的事件监视**:`poll` 使用 `revents` 字段单独返回事件,避免了 `select` 中“传入-传出”参数合一的尴尬,使用起来更清晰。1. **文件描述符数量限制**:`poll` 使用一个动态数组(由`struct pollfd *`指向),理论上只受系统内存和进程能打开的文件描述符总数限制。

Loving_enjoy的博客 198

select源码剖析(Linux2.6.11)

select是在一定时间内,监听用户注册的可读、可写、异常事件。它的可读、可写、异常事件分别对应着文件描述符的集合。当有事件发生的时候,内核就会修改这些参数来告诉应用程序哪些文件描述符以及就绪了。这样的话下次调用select时就需要重新设置可读、可写、异常事件的文件描述符。 select系统调用的顺序是: select () -&gt; sys_select() -&gt; do_select...

静沐暖阳的博客 671

STM32_WS2811驱动

STM32的WS2811驱动

健身房 2840

别再卡在Google验证了!用Header Editor插件5分钟搞定TryHackMe注册(附最新.json规则)

本文提供了使用Header Editor插件快速解决TryHackMe注册时Google验证失败问题的详细指南。通过导入特定的JSON规则文件,用户可以轻松绕过网络限制,5分钟内完成账号注册,无需复杂设置。文章包含完整的配置步骤、常见问题解决方案以及安全使用建议。

weixin_30736301的博客 463

Linux Select 工作原理深度剖析: 从设计思想到实现细节

设计哲学简单统一的文件描述符抽象同步阻塞的轮询模型位图数据结构的巧妙应用工作流程用户空间准备fd_set → 系统调用进入内核 → 内核复制数据 →遍历所有fd调用poll → 等待或返回 → 结果复制回用户空间 →用户遍历检查结果性能特点连接数少时性能可接受连接数多时性能急剧下降内存复制和双重遍历是主要瓶颈。

X 1102

博途S7-1200与昆仑通态MCGS纯仿真联调实战指南

本文详细介绍了博途S7-1200与昆仑通态MCGS纯仿真联调的实战指南,包括环境搭建、配置步骤、通信桥接及故障排除。通过TIA Portal和MCGS Pro搭建的仿真环境,可实现80%以上的基础功能验证,适用于工业自动化学习和开发,大幅降低硬件投入成本。

weixin_26834815的博客 439

【Linux】select 多路转接深度剖析:从位图原理到字典服务器实现

本文深入剖析了select多路转接机制,从基础原理到实际应用进行全面讲解。首先通过班主任管理班级的类比,形象说明select的核心功能是同时监控多个文件描述符。然后详细解析select函数原型、参数和返回值,重点分析fd_set位图的数据结构及其操作宏。文章揭示了select的固有缺陷,包括1024个文件描述符的监控上限和每次调用需重新设置集合的性能损耗。最后通过具体示例逐步拆解select的执行流程,展示其如何在内核层面实现多路IO监控。这些内容为后续理解更高效的epoll机制奠定了重要基础。

半截诗的博客~ 1223

宇树G1开发实践--PPT汇报(遥控版)

本文介绍了一种基于遥控器交互的机器人演示控制系统。系统通过DDS中间件接收遥控器信号,采用位掩码边沿检测技术实现按键精准响应,将不同按键映射为播放控制命令(暂停/重播、跳转、跳过等)。核心交互逻辑包含主播放循环、命令处理和暂停机制,支持播放过程中的实时控制。与自动顺序播放版相比,该系统显著提升了演示灵活性,适用于需要人机互动的演讲场景。使用前需确保网络连接和设备安全,并熟悉按键功能映射。该系统实现了专业级的人机协同演示体验。

m0_47999117的博客 680

ISO2626 --- Item Definition相关项定义

本文并不是针对ISO26262的一个项目,而是针对ISO26262的规范,我们在相关项定义阶段,我们需要做什么,我们的文档需要包含哪些内容的一个描述。

wj博客 909

【Linux】I/O 多路转接:select & epoll 技术剖析

本文详细介绍了 Linux 下的 I/O 多路复用技术,包括 select、poll 和 epoll 的原理、优缺点及使用场景。select 和 poll 适用于少量连接,而 epoll 在处理大量并发连接时表现出色,文章通过示例代码展示了如何使用这些技术实现服务器程序,强调了根据实际需求选择合适的 I/O 模型的重要性。

You build it, you run it 3868

MATLAB应用实战系列(四十三)-基于MATLAB的光伏并网系统仿真设计

在Matlab中建立光伏电站接入系统模型,包括光伏发电逆变器及负荷模型等,仿真分析接入点处的电能质量,实现高品质并网运行。 壹 构建太阳能电池模型。 初始化参数如下图所示。 贰 构建MPPT控制模型。 具体封装如下所示: 通过PWM算法实现DC-DC稳步输出最大点。 叁 三、构建直流短距离输电线路。 仿真模型如下图: 通过电容来均衡线路电压,使得线路电压保持可靠性。 肆 通过逆变器变换,使得直流线路转换成...

wenyusuran的专栏 3169

linux 内核poll/select/epoll实现剖析(原理经典)

转自:https://blog.youkuaiyun.com/lishenglong666/article/details/45536611 poll/select/epoll的实现都是基于文件提供的poll方法(f_op-&gt;poll), 该方法利用poll_table提供的_qproc方法向文件内部事件掩码_key对应的的一个或多个等待队列(wait_queue_head_t)上添加包含唤醒函数(w...

xcw_1987的博客 5293

22、嵌入式软件随机延迟生成与加密算法防护技术

本文探讨了嵌入式软件中的随机延迟生成方法与块密码软件实现的高阶掩码及洗牌技术。重点介绍了浮动均值延迟生成方法,其在8位AVR平台上的实现效率为Θ(1),显著提升了抗相关功率分析(CPA)攻击的能力,相比传统方法更安全且轻量。同时,分析了高阶掩码与洗牌技术结合的方案,通过理论建模量化其对高级差分功率分析(DPA)攻击的抵抗能力,并以AES为例展示了该方案的应用流程。最后对比了不同防护技术在安全性与开销之间的权衡,为实际应用中选择合适的安全策略提供了依据。未来可进一步优化实现效率并融合多种防护手段以应对复杂安全

fern8的博客 88

CodeX 登录报错 os error 10013?一键修复 CodeX 无法登录问题(亲测有效)

CodeX 登录时报 os error 10013?本文提供快速修复方案,实测有效,彻底解决登录报错:登录失败:failed to start login server: 以一种访问权限不允许的方式做了一个访问套接字的尝试。 (os error 10013)的问题💡

XiaoqiangClub的博客 2926

Linux 系统调用之 select 源码剖析

Linux 中 select 实现的核心逻辑 对 Linux 2.6.36 中 select 的代码简化如下,只列出了关键步骤以展示核心逻辑。 SYSCALL_DEFINE5(select, int, n, fd_set *, inp, fd_set *, outp, fd_set *, exp, struct timeval *, tvp) { if (tvp) { /* 判断是否设置超时时间 */ if (copy_from_user(&tv, tvp, sizeof(tv)))

m0_47696151的博客 767

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

上一篇: 常见通信协议总结
下一篇: stat 详细介绍及用法
chen_ever
博客等级 码龄4年 497粉丝 78原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值