select,poll,epoll

1. Epoll是何方神圣?linux

Epoll但是当前在Linux下开发大规模并发网络程序的热门人选,Epoll Linux2.6内核中正式引入,和select类似,其实都I/O多路复用技术而已,并无什么神秘的。数组

 

其实在Linux下设计并发网络程序,向来不缺乏方法,好比典型的Apache模型(Process Per Connection,简称PPC),TPCThread PerConnection)模型,以及select模型和poll模型,那为什么还要再引入Epoll这个东东呢?那仍是有得说说的服务器

2. 经常使用模型的缺点网络

若是不摆出来其余模型的缺点,怎么能对比出Epoll的优势呢。数据结构

2.1 PPC/TPC模型并发

这两种模型思想相似,就是让每个到来的链接一边本身作事去,别再来烦我。只是PPC是为它开了一个进程,而TPC开了一个线程。但是别烦我是有代价的,它要时间和空间啊,链接多了以后,那么多的进程/线程切换,这开销就上来了;所以这类模型能接受的最大链接数都不会高,通常在几百个左右。异步

2.2 select模型socket

1. 最大并发数限制,由于一个进程所打开的FD(文件描述符)是有限制的,由FD_SETSIZE设置,默认值是1024/2048,所以Select模型的最大并发数就被相应限制了。本身改改这个FD_SETSIZE?想法虽好,但是先看看下面吧ide

2. 效率问题,select每次调用都会线性扫描所有的FD集合,这样效率就会呈现线性降低,把FD_SETSIZE改大的后果就是,你们都慢慢来,什么?都超时了??!!函数

3. 内核/用户空间内存拷贝问题,如何让内核把FD消息通知给用户空间呢?在这个问题上select采起了内存拷贝方法,每次调用select,都须要把fd集合从用户态拷贝到内核态。

2.3 poll模型

基本上效率和select是相同的,select缺点的23它都没有改掉。

3. Epoll的提高

把其余模型逐个批判了一下,再来看看Epoll的改进之处吧,其实把select的缺点反过来那就是Epoll的优势了。

3.1. Epoll没有最大并发链接的限制,上限是最大能够打开文件的数目,这个数字通常远大于2048, 通常来讲这个数目和系统内存关系很大,具体数目能够cat /proc/sys/fs/file-max察看。

3.2. 效率提高,Epoll最大的优势就在于它只管你“活跃”的链接,而跟链接总数无关,所以在实际的网络环境中,Epoll的效率就会远远高于selectpoll

3.3. 内存拷贝,Epoll在这点上使用了“共享内存”,这个内存拷贝也省略了。

 

4. Epoll为何高效

Epoll的高效和其数据结构的设计是密不可分的,这个下面就会提到。

首先回忆一下select模型,当有I/O事件到来时,select通知应用程序有事件到了快去处理,而应用程序必须轮询全部的FD集合,测试每一个FD是否有事件发生,并处理事件

Epoll不只会告诉应用程序有I/0事件到来,还会告诉应用程序相关的信息,这些信息是应用程序填充的,所以根据这些信息应用程序就能直接定位到事件,而没必要遍历整个FD集合。


6. 使用Epoll

既然Epoll相比select这么好,那么用起来如何呢?会不会很繁琐啊先看看下面的三个函数吧,就知道Epoll的易用了。

 

intepoll_create(int size);

生成一个Epoll专用的文件描述符,实际上是申请一个内核空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个Epoll fd上能关注的最大socket fd数,大小自定,只要内存足够。

intepoll_ctl(int epfd, intop, int fd, structepoll_event *event);

控制某个Epoll文件描述符上的事件:注册、修改、删除。其中参数epfdepoll_create()建立Epoll专用的文件描述符。相对于select模型中的FD_SETFD_CLR宏。

intepoll_wait(int epfd,structepoll_event * events,int maxevents,int timeout);

等待I/O事件的发生;参数说明:

epfd:epoll_create() 生成的Epoll专用的文件描述符;

epoll_event:用于回传代处理事件的数组;

maxevents:每次能处理的事件数;

timeout:等待I/O事件发生的超时值;

返回发生事件数。

相对于select模型中的select函数。


 

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Epoll模型主要负责对大量并发用户的请求进行及时处理,完成服务器与客户端的数据交互。其具体的实现步骤以下:
(a) 使用epoll_create()函数建立文件描述,设定将可管理的最大socket描述符数目。
(b) 建立与epoll关联的接收线程,应用程序能够建立多个接收线程来处理epoll上的读通知事件,线程的数量依赖于程序的具体须要。
(c) 建立一个侦听socket描述符ListenSock;将该描述符设定为非阻塞模式,调用Listen()函数在套接字上侦听有无新的链接请求,在 epoll_event结构中设置要处理的事件类型EPOLLIN,工做方式为 epoll_ET,以提升工做效率,同时使用epoll_ctl()注册事件,最后启动网络监视线程。
(d) 网络监视线程启动循环,epoll_wait()等待epoll事件发生。
(e) 若是epoll事件代表有新的链接请求,则调用accept()函数,将用户socket描述符添加到epoll_data联合体,同时设定该描述符为非阻塞,并在epoll_event结构中设置要处理的事件类型为读和写,工做方式为epoll_ET.
(f) 若是epoll事件代表socket描述符上有数据可读,则将该socket描述符加入可读队列,通知接收线程读入数据,并将接收到的数据放入到接收数据的链表中,经逻辑处理后,将反馈的数据包放入到发送数据链表中,等待由发送线程发送。

 

1.不要采用一个链接一个线程的方式,而是尽可能利用操做系统的事件多路分离机制
如:UNIX下的 select  linux下的epoll BSD下的kqueue
或者使用这些机制的高层API (boost.asio&&ACE Reactor)
2.尽可能使用异步I/O,而不是同步
3.当事件多路分离单线程没法知足并发需求时,将事件多路分离的线程扩展成线程池  

两种方式的区别主要体如今如下几个方面:

  1. select所能控制的I/O数有限,这主要是由于fd_set数据结构是一个有大小的,至关与一个定长所数组。

  2. select每次都须要从新设置所要监控的fd_set(由于调用以后会改变其内容),这增长了程序开销。

  3. select的性能要比epoll差,具体缘由会在后续内容中详细说明。

嗯,说道这个为何select要差,那就要从这个select API提及了。这个传进去一个数组,内部实现也不知道那个有哪一个没有,因此要遍历一遍。假设说我只监控一个文件描述符,可是他是1000。那么select须要遍历前999个以后再来poll这个1000的文件描述符,而epoll则不须要,由于在以前epoll_ctl的调用过程当中,已经维护了一个队列,因此直接等待事件到来就能够了。

epoll跟select都能提供多路I/O复用的解决方案。在如今的Linux内核里有都可以支持,其中epoll是Linux所特有,而select则应该是POSIX所规定,通常操做系统均有实现

select:

select本质上是经过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:

一、 单个进程可监视的fd数量被限制,即能监听端口的大小有限。

      通常来讲这个数目和系统内存关系很大,具体数目能够cat /proc/sys/fs/file-max察看。32位机默认是1024个。64位机默认是2048.

二、 对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低:

       当套接字比较多的时候,每次select()都要经过遍历FD_SETSIZE个Socket来完成调度,无论哪一个Socket是活跃的,都遍历一遍。这会浪费不少CPU时间。若是能给套接字注册某个回调函数,当他们活跃时,自动完成相关操做,那就避免了轮询,这正是epoll与kqueue作的。

三、须要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大

poll:

poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,而后查询每一个fd对应的设备状态,若是设备就绪则在设备等待队列中加入一项并继续遍历,若是遍历完全部fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了屡次无谓的遍历。

它没有最大链接数的限制,缘由是它是基于链表来存储的,可是一样有一个缺点:

一、大量的fd的数组被总体复制于用户态和内核地址空间之间,而无论这样的复制是否是有意义。                                                                                                                                      二、poll还有一个特色是“水平触发”,若是报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

epoll:

epoll支持水平触发和边缘触发,最大的特色在于边缘触发,它只告诉进程哪些fd刚刚变为就需态,而且只会通知一次。还有一个特色是,epoll使用“事件”的就绪通知方式,经过epoll_ctl注册fd,一旦该fd就绪,内核就会采用相似callback的回调机制来激活该fd,epoll_wait即可以收到通知

epoll的优势:


一、没有最大并发链接的限制,能打开的FD的上限远大于1024(1G的内存上能监听约10万个端口);
二、效率提高,不是轮询的方式,不会随着FD数目的增长效率降低。只有活跃可用的FD才会调用callback函数;
      即Epoll最大的优势就在于它只管你“活跃”的链接,而跟链接总数无关,所以在实际的网络环境中,Epoll的效率就会远远高于select和poll。

三、 内存拷贝,利用mmap()文件映射内存加速与内核空间的消息传递;即epoll使用mmap减小复制开销。

select、poll、epoll 区别总结:


一、支持一个进程所能打开的最大链接数

select

单个进程所能打开的最大链接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是32*32,同理64位机器上FD_SETSIZE为32*64),固然咱们能够对进行修改,而后从新编译内核,可是性能可能会受到影响,这须要进一步的测试。

poll

poll本质上和select没有区别,可是它没有最大链接数的限制,缘由是它是基于链表来存储的

epoll

虽然链接数有上限,可是很大,1G内存的机器上能够打开10万左右的链接,2G内存的机器能够打开20万左右的链接

二、FD剧增后带来的IO效率问题

select

由于每次调用时都会对链接进行线性遍历,因此随着FD的增长会形成遍历速度慢的“线性降低性能问题”。

poll

同上

epoll

由于epoll内核中实现是根据每一个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,因此在活跃socket较少的状况下,使用epoll没有前面二者的线性降低的性能问题,可是全部socket都很活跃的状况下,可能会有性能问题。

三、 消息传递方式

select

内核须要将消息传递到用户空间,都须要内核拷贝动做

poll

同上

epoll

epoll经过内核和用户空间共享一块内存来实现的。

总结:

综上,在选择select,poll,epoll时要根据具体的使用场合以及这三种方式的自身特色。

一、表面上看epoll的性能最好,可是在链接数少而且链接都十分活跃的状况下,select和poll的性能可能比epoll好,毕竟epoll的通知机制须要不少函数回调。

二、select低效是由于每次它都须要轮询。但低效也是相对的,视状况而定,也可经过良好的设计改善

相关文章
相关标签/搜索