点星之人

成长的足迹

select和epoll对比

select和epoll对比
什么是epoll模型?
    epoll是Linux内核为处理大批量句柄而作了改进的poll,
    是Linux下多路复用IO接口select/poll的增强版本,
    它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。
    因为它会复用文件描述符集合来传递结果,
    而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,
    另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,
    只要遍历那些被内核I/O事件异步唤醒而加入Ready队列的描述符集合就行了
epoll的操作总共过4个API:epoll_create, epoll_ctl, epoll_wait和close。

select:
    性能:随着连接数增加,急剧下降。处理成千上万并发连接数时,性能很差。
    连接数:连接数有限制,处理的最大连接数不超过1024。如果处理连接数超过1024,
            需要修改系统内核设置的FD_SETSIZE值,并重新编译。
    内在处理机制:线性轮询
    开发复杂性:低
epoll
    性能:随着连接数的增加,性能基本上没有下降。处理成千上万并发连接时,性能很好。
    连接数:连接无限制。
    内在处理机制:回调callback
    开发复杂性:中

select的特点:select 选择句柄的时候,是遍历所有句柄,也就是说句柄有事件响应时,
    select需要遍历所有句柄才能获取到哪些句柄有事件通知,因此效率是非常低。
    但是如果连接很少的情况下, select和epoll的LT触发模式相比, 性能上差别不大。
    这里要多说一句,select支持的句柄数是有限制的, 同时只支持1024个,
    如果超过这个限制,很可能导致溢出,而且非常不容易发现问题,
    当然可以通过修改linux的socket内核调整这个参数。
epoll的特点:epoll对于句柄事件的选择不是遍历的,是事件响应的,
    就是句柄上事件来就马上选择出来,不需要遍历整个句柄链表,因此效率非常高,
    内核将句柄用红黑树保存的。对于epoll而言还有ET和LT的区别,LT表示水平触发,
    ET表示边缘触发,两者在性能以及代码实现上差别也是非常大的。

Nginx采用epoll模型,异步非阻塞。对于Nginx来说,
     把一个完整的连接请求处理都划分成了事件,一个一个的事件。
     比如accept(), recv(),磁盘I/O,send()等,每部分都有相应的模块去处理,
     一个完整的请求可能是由几百个模块去处理。真正核心的就是事件收集和分发模块,
     这就是管理所有模块的核心。只有核心模块的调度才能让对应的模块占用CPU资源,从而处理请求。
     拿一个HTTP请求来说,首先在事件收集分发模块注册感兴趣的监听事件,
     注册好之后不阻塞直接返回,接下来就不需要再管了,
     等待有连接来了内核会通知你(epoll的轮询会告诉进程),cpu就可以处理其他事情去了。
     一旦有请求来,那么对整个请求分配相应的上下文(其实已经预先分配好),
     这时候再注册新的感兴趣的事件(read函数),
     同样客户端数据来了内核会自动通知进程可以去读数据了,读了数据之后就是解析,
     解析完后去磁盘找资源(I/O),一旦I/O完成会通知进程,进程开始给客户端发回数据send(),
     这时候也不是阻塞的,调用后就等内核发回通知发送的结果就行。
     整个下来把一个请求分成了很多个阶段,每个阶段都到很多模块去注册,然后处理,都是异步非阻塞。
     异步这里指的就是做一个事情,不需要等返回结果,做好了会自动通知你。

select,poll,epoll都是IO多路复用的机制。
    I/O多路复用就通过一种机制,可以监视多个描述符,
    一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
    但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,
    也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,
    异步I/O的实现会负责把数据从内核拷贝到用户空间。
此时需知道两个概念:
所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,
    如果事件没有发生,进程或线程就被阻塞,函数不能立即返回。
所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,
    一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,
    若事件没有发生,则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高。

查看原文:http://www.chenqmc.com/?p=442
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/selina361/article/details/79949677
上一篇Apache与Nginx的优缺点比较
下一篇几种常见负载均衡比较
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭