select poll epoll

Select

select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,select在socket编程中还是比较重要的。一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常!!

select模型的特点:
(1) FD连接数量:单个进程可监视的fd数量被限制(支持最大连接数1024(x86) or 2048(x64));
(2) I/O效率:返回的可读集合是个fdset类型,需要对所有的监听读句柄一一进行FD_ISSET的测试来判断是否可读; 对描述符进行扫描时是线性扫描,比较耗时,时间复杂度线性增加,每次调用进行线性遍历,时间复杂度为O(N)。比如如果只监听0和1000两个句柄,select需要遍历1001个句柄来检查事件。
select需要对所有的监听读句柄扫描判断是否是自己关心的一一进行FD_ISSET的测试来判断是否可读,是否发生了事件;
(3) 空间效率:FD每次都全部拷贝。需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大

Poll

poll模型的特点:
(1) FD连接数量:Poll没有最大连接数的限制,
(2) I/O效率:poll运行过程中,也是将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历,每次调用进行线性遍历,时间复杂度为O(N);原因是它是基于链表来存储的,但是同样有一个缺点:大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
(3)空间效率:FD每次poll都拷贝。

1、和select相比,poll的优点是:
(1) 不再局限于FD_SETSIZE个监听描述符,只要能打开的描述符,都可以监听;
(2) 监听描述符集合不再是值结果参数,而是event表示监听事件,revents表示触发的事件;
(3) poll的效率比select稍高(poll只遍历输入的监听数组中的描述符,如果数组中的fd<0,则poll忽略fd,当监听的描述符离散时效率稍高于select。比如监听0和1000两个句柄,则poll只需要遍历两个描述符,而select需要遍历1001个描述符;当监听描述符连续时,poll和select效率相当,底层实现也是一致的)。

解决了select中的问题(1),一定程度上也解决了(2),比如如果只监听0和1000两个句柄,select需要遍历2个描述符来检查事件,而select需要1001次。

2、poll和select共同的问题是性能较差:

(1)遍历所有的文件描述符,当监听描述符个数增加时,监听效率降低,
(2)并且select和poll每次都要在用户态和内核态拷贝监听的描述符参数。

Epoll

  epoll是为处理大批量句柄而作了改进的poll。是提高内核I/O性能的新方法。当同时需要保持很多的长连接,而且连接的开关很频繁时,就能够发挥epoll最大的优势了。在一些 benchmark中,如果所有的socket基本上都是活跃的---比如一个高速LAN环境,epoll并不比select/poll有什么效率,相 反,如果过多使用epoll_ctl,效率相比还有稍微的下降。但是一旦使用idle connections模拟WAN环境,空闲的连接量十分大,偶尔蹦出来几个活跃的客户端,epoll的效率就远在select/poll之上了。它是在2.5.44内核中被引进的:
  1. int epoll_create(int size);
    注:当创建好epoll句柄后,它就是会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

  2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    epoll的事件注册函数,它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。
    第一个参数是epoll_create()的返回值。
    第二个参数表示动作,用三个宏来表示:
    EPOLL_CTL_ADD:注册新的fd到epfd中;
    EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
    EPOLL_CTL_DEL:从epfd中删除一个fd;
    第三个参数是需要监听的fd。
    第四个参数是告诉内核需要监听什么事,struct epoll_event结构

  3. int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
    收集在epoll监控的事件中已经发送的事件。参数events是分配好的epoll_event结构体数组,epoll将会把发生的事件赋值到events数组中(events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时。

  4. 相比与select和poll模型,epoll模型的优点:
    (1) FD连接数量:Epoll它没有最大连接数的限制,只受进程打开描述符总数的限制;
    (2)I/O效率:epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,返回参数中就是触发事件的列表,不用再遍历输入事件表查询各个事件是否被触发。内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。时间发复杂度O(1)
    (3)空间效率:调用epoll_ctl时拷贝进内核并由内核保存,之后每次epoll_wait不拷贝,使用共享内存的方式,不在用户和内核之间反复传递监听的描述符信息;epoll使用 mmap减少复制开销。
    (4)监听性能不随着监听描述符数的增加而增加,是O(1)的,不再是轮询描述符来探测事件,而是由描述符主动上报事件;
    epoll显著提高性能的前提是:监听大量描述符,并且每次触发事件的描述符文件非常少。
  5. select、poll和epoll性能PK
    (1)、epoll模型的最大特点就是没有setfpdsize大小的限制;
    而select有大小的限制1024个;可以在修改内核的大小,但是资料显示这个会导致效率下降;还可以选择多进程 ,但是多进程之间的数据的同步和开销在大量的连接请求上的效率是比较低下的;可以在ect/ frofile/maxfile中查看最大的数量,这个值一般和内存有很大的关系,比如1GB的内存大概有10万个FD文件描述符,句柄;
    (2)、IO 效率不随FD数目增加而线性下降
    二是基于事件的通知机制而不是线性扫描,内核采用回调函数callback来调用相应的处理函数。传统的select和poll模型中进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,并且会随着fd数量的增加而使其效率线性下降,因为poll和select是线性的扫描各个文件描述符来实现的;而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。
    (3)、使用mmap加速内核 与用户空间的消息传递。

采用mmap技术省去了结构体FD从用户态向内核态空间复制的时间内存开销。epoll模型使用mmap加速内核与用户之间的消息传递;避免了传统的select、和poll的一些不必要的内存拷贝,类似fork的(Copy-On-Write)COW技术一样,epoll和用户是公用一块内存空间实现的;
注:使用mmap加速内核 与用户空间的消息传递。mmap是将一个文件和对象映射到内存下;

四、epoll的两种工作支持水平触发和边沿触发。

       LT(Level Triggered) 水平触发是epoll缺省的工作方式,并且同时支持block和no-block socket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表.

       ET (edge-triggered) 是高速工作方式,只支持no-block socket,它效率要比LT更高(该模式的工作原理决定了它只能支持非阻塞模式)。ET与LT的区别在于,当一个新的事件到来时,ET模式下当然可以从epoll_wait调用中获取到这个事件,可是如果这次没有把这个事件对应的套接字缓冲区处理完,在这个套接字中没有新的事件再次到来时,在ET模式下是无法再次从epoll_wait调用中获取这个事件的。而LT模式正好相反,只要一个事件对应的套接字缓冲区还有数据,就总能从epoll_wait中获取这个事件。因此,LT模式下开发基于epoll的应用要简单些,不太容易出错。而在ET模式下事件发生时,如果没有彻底地将缓冲区数据处理完,则会导致缓冲区中的用户请求得不到响应。在LT-水平触发和缺省模式下,只要是该缓冲区中有数据,则总是能从epoll_wait中获取该事件。显的就比较安全。

简而言之:

  水平触发(level-triggered)——只要满足条件,就触发一个事件(只要有数据没有被获取,内核就不断通知你);

   边缘触发(edge-triggered)——每当状态变化时,触发一个事件。

例如:
1. 我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符
2. 这个时候从管道的另一端被写入了2KB的数据
3. 调用epoll_wait(2),并且它会返回RFD,说明它已经准备好读取操作
4. 然后我们读取了1KB的数据
5. 调用epoll_wait(2)……

     若是LT工作方式,内核会继续通知你RFD句柄就绪,里面还有1KB的数据没有读处理;但是若是ET工作方式,内核将只会通知一遍,剩下的IKB数据在下一RFD句柄没有新的活动(数据的发送或接受)的情况下,就不会被应用程序处理,造成请求得不到响应。所以对于ET触发的,fd必须是非阻塞式的,这样可以一次把数据读完,如果是阻塞式的,说不定则会读到阻塞;

五、epoll使用注意问题:

注意一:
int epoll_create(int size); //创建epoll文件描述符
参数size并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。返回是epoll描述符。-1表示创建失败。

注意二:
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
//等待epfd上的io事件,最多返回maxevents个事件

注意三:
epoll监听ET事件时,fd必须是非阻塞套接口。比如监听可读事件,当ET上报可读后,需要一直读fd直到遇到EAGAIN错误为止,以免遗留数据在缓冲区中。如果fd是阻塞的, 则会读到阻塞了。

EAGAIN错误对于非阻塞套接口来说不是错误,只是说没有数据可读或者没有空间可写。

EWOULDBLOCK就是EAGAIN,值都是11。

selset/poll/epoll的LT模式监听的fd可以是阻塞模式的(因为只要满足条件,数据没有读完,内核就会持续通知你)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值