多路转接——select、poll、epoll

首先要明确一点那就是IO执行的两个阶段 :    1.等待数据准备好      2.从内核中进行数据拷贝

在实现的时候分为同步IO:在调用的没有得到结果前,该调用不返回,调用者主动等待这个调用的结果     异步IO:调用发出后,直接返回,不会立刻得到结果,而是让被调用者通知调用者

Linux下的五种IO模型:阻塞、非阻塞、信号驱动、多路复用、异步IO。其中,前四种都是同步IO,最后一种是异步IO

阻塞IO和非阻塞IO:

其中,阻塞IO顾名思义就是指没有结果返回时,线程会被暂时挂起,直到满足一定条件,得到结果才会返回。而我们所了解的文件描述符,默认都是阻塞IO。

非阻塞IO是指若不能马上得到结果,该调用不会阻塞当前线程,而是处理其他事物。想将一个文件描述符设置为非阻塞的,就要用到fantl函数:

               

多路转接:

       Linux系统提供了几种实现多路复用的方式,可以使一个进程监视多个IO。实现原理:用户告诉内核想要监视的文件描述符所发生的事务(读、写、错误),当这些事务就绪后,内核通知用户。而以下几种多路复用的方式只能够实现IO的第一步:等待内核中的数据就绪。    

一:select

    select是实现多路复用的一个接口,当系统调用select时,程序会等待直到被监视的文件描述符被改变后返回,先来看一下select函数的原型: 

                                                  

   nfds是指需要监视的最大文件描述符+1,后面三个参数是输入/输出型参数,fd_set是用位图实现的,将被监视的文件描述符添加进去,就绪后返回该位图,这就代表了传入的fd_set和返回的fd_set内容发生了改变,所以每次监视的时候都需要重置fd_set。系统提供了一组函数供用户操作位图,以下是一个用到select的服务器demo。

select的缺点:

  • 每次select的时候,都需要重新设置fd_set集合
  • 每次select时,都需要将fd_set从用户态拷贝到内核态,因为需要系统去监视文件描述符集
  • 当随着所监视的文件描述符集增多的时候,效率会降低,因为需要遍历使得开销增大
  • select中的fd_set所监视的文件描述符是有限的的,为1024

    select的服务器demo:https://github.com/MotiStu/Linux_code/blob/master/IO/select_server.cc 

二:poll 

     select中的fd_set用位图来实现,并且作为输入/输出性参数中的不便,poll中就改变了它的fd。poll的函数原型中,其中fds是一个结构体用来监视文件描述符发生的事件,其中fd是文件描述符,events是想要的事件(读、写、异常),revents是用来返回什么事件就绪。将其分开来构成一个结构体,使用起来更直观方便。但是并没有更好的解决select中出现的问题。

poll的缺点:

  • 调用poll的时候,也需要将fd从用户态拷贝到内核态
  • 和select一样,每次都需要遍历查找就绪的fd
  • 虽然没有select的数量限制,但数量过大后性能也会降低

  poll的服务器demo:https://github.com/MotiStu/Linux_code/blob/master/IO/poll_server.cc   

三:epoll                                               

     前两种的多路复用方式都没有很好的解决出现的问题,就有了升级版的epoll,其中,epoll_create是用来创建一个epoll句柄,epoll_ctl是对监听文件描述符的操作,epoll_wait则是等待就绪的事件。                                                             

  • epoll_create:创建了一个epoll模型,由就绪队列、红黑树、回调机制构成,很好的解决了select和poll的缺点
  • epoll_ctl:将要监视的文件描述符注册,树中的每一个节点都是想要监视文件描述符中的事件,所以每次注册新的事件的时候,会把所有的事件拷到内核区,所以epoll_wait时就不用重复拷贝了,保证了每个fd只用拷贝一次到内核区中
  • epoll_wait:等待文件描述符就绪,用到了回调机制将发生的事件放到就绪队列中,检查事件就绪就只需要看就绪队列中有没有节点,解决了之前每次都需要轮询一遍的问题。

epoll的优点:

  1. 文件描述符无上限,因为内核中用红黑树来管理
  2. 当文件描述符就绪后,内核会用回调机制将其放到就绪队列中,这样epoll_wait获取时,时间复杂度为O(1)。
  3. 内核直接将就绪队列通过mmap映射到了用户态,避免拷内的额外开销

      *mmap是一种映射机制:将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。内核空间对这段区域的修改直接反映给用户空间。

      epoll的服务器demo:https://github.com/MotiStu/Linux_code/blob/master/IO/epoll_server.cc

epoll的两种模式:

  • 水平触发(LT)当epoll检测到socket上事件就绪的时候,可以不立刻进行处理.或者只处理一部分有就绪事件发生时,若一次处理后缓冲区还剩余数据,第二次调用epoll_wait时,会立即返回并通知事件就绪,直到缓冲区中的数据处理完。

  • 边沿触发(ET):当检测到时间就绪的时候,必须马上处理若缓冲区中还剩有数据,下一次epoll_wait不会返回,而是有新的数据包来了才返回。也就是说,ET模式下,文件描述符上的事件就绪后,只有一次处理机会只支持非阻塞读ET(边缘触发)数据就绪只会通知一次,也就是说,如果要使用ET模式,当数据就绪时,需要一直read,直到出错或完成为止

  • ET的效率比LT的效率高,(返回次数少了很多)

  • select和poll都工作在LT模式下,而epoll可以是LT和ET

 

 

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值