查看select、poll和epoll的实现请看这里:
select的实现:https://blog.csdn.net/qq_39110766/article/details/80041451
poll的实现:https://blog.csdn.net/qq_39110766/article/details/80042768
epoll的实现:https://blog.csdn.net/qq_39110766/article/details/80041735
一.Select
1.Select函数监视的描述符分为3类,分别是writefds,readfds和exceptfds.调用后select函数会阻塞,知道描述符就绪(可读,可写)或者超时(timeout指定等待时间,如果立即范围设为null),函数返回.
2.Select的本质就是通过设置或者检查存放fd标志位的数据结构来进行下一步的处理.
3.单个进程能过监视的进程描述符有最大值,一般在linux上为1024,可以通过修改宏定义或者诚信编译内核的方法提升这一限制,不过它这样也会造成效率的降低.
4.select采用轮询的方式扫描文件描述符,文件描述符越多,处理性能越差.
select缺点总结:
缺点:
1.每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;
2.每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大;(时间复杂度为O(N));
3.单个进程能够监视的文件描述符存在最大的限制。
二.Poll
1.Poll和select在本质上没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中介入意向并继续遍历,如果遍历完所有fd没有发现就绪设备,则挂起当前进程,知道设备就绪或者主动超时,被唤醒后又要再次遍历fd,这个过程经历了很多次无谓的遍历.
2.相比于select,poll采用链表的方式保存文件描述符,从而没有了监视文件数量控制的问题,但它同样存在问题,比如说将大量的fd数组整体复制于用户态和内核态地址之间,而不管这样的赋值有没有用 ,还有就是poll 特点,就是水平触发,如果报告了fd后,没有被处理,那么下次poll会再次报告该fd.
poll的优点和缺点总结:
缺点
包含大量的文件描述符的数组被整体复制于用户态与内核态之间,而无论文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。(时间复杂度为O(N));
优点
没有最大文件描述符数量的限制(不过随着文件描述符数量的增加,它的性能也会下降)。
三.Epoll
1.相对于select和poll来说,epoll更加灵活,没有描述符限制。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中,这样在用户空间和内核空间的copy只需一次。
2.epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。
epoll的优点总结:
优点:
1.没有最大并发连接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口)。
2、效率提升,不是轮询的方式,不会随着FD数目的增加效率下降。
只有活跃可用的FD才会调用callback函数;即Epoll最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll的效率就会远远高于select和poll。
3、内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减少复制开销。
epoll对文件描述符的操作有两种模式:LT(level trigger)和ET(edge trigger)。LT模式是默认模式
LT模式与ET模式的区别如下:
LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。
ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。
1、LT模式
LT(level triggered)是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的。
2、ET模式
ET(edge-triggered)是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK 错误)。但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。
ET模式在很大程度上减少了epoll事件被重复触发的次数,因此效率要比LT模式高。epoll工作在ET模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
3、在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。(此处去掉了遍历文件描述符,而是通过监听回调的的机制。这正是epoll的魅力所在。)
注意:如果没有大量的idle-connection或者dead-connection,epoll的效率并不会比select/poll高很多,但是当遇到大量的idle-connection,就会发现epoll的效率大大高于select/poll。
说这么多好烦啊!!!看图吧!更明了一点:
Select,poll和epoll的区别:
1、 支持一个进程所能打开的最大连接数
2、FD剧增后带来的IO效率问题
3、消息传递方式