select 和epoll

select和epoll出现的背景
    对一个文件描述符指定的文件或设备,有两种工作方式 :阻塞与非阻塞。
    所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不

可写, 程序就进入等待状态, 直到有东西可读或者可写为止。而对于非阻塞状态, 如果没有东西可读, 或

者不可写, 读写函数马上返回, 而不会等待。缺省情况下, 文件描述符处于阻塞状态。
    在实现聊天室时, server 需要轮流查询与各client 建立的 socket, 一旦可读就将该 socket 中的

字符读出来并向所有其他client 发送。并且, server 还要随时查看是否有新的 client 试图建立连接,

这样, 如果 server 在任何一个地方阻塞了, 其他 client 发送的内容就会受到影响,得不到服务器的及

时响应。新 client 试图建立连接也会受到影响。所以我们在这里不能使用缺省的阻塞的文件工作方式,

而需要将文件的工作方式变成非阻塞方式。
     分析上面的程序可以知道, 不管是 server 还是 client, 它们都不停的轮流查询各个文件描述符,

一旦可读就读入并进行处理. 这样的程序, 不停的在执行, 只要有CPU 资源, 就不会放过。因此对系统资

源的消耗非常大。server 或者 client 单独执行时, CPU 资源的 98% 左右都被其占用。极大的消耗了系

统资源。
     因此,虽然我们不希望在某一个用户没有反应时阻塞其他的用户,但我们却应该在没有任何用户有

反应的情况之下停止程序的运行,让出抢占的系统资源,进入阻塞状态。
select和epoll机制的比较
总的一句话概括是:
select采用轮循而epoll采用回调,触发式的,所以效率高
1 从机制上进行分析
select的第一个参数numfds为fd_set集合中最大描述符值加一,表示fd_set这个数组的大小,其大小限制

为FD_SETSIZE(1024)
select的第二三四个参数表示需要关注的读,写,例外的状况,在readfds中列出的文件描述符被监视是

否有数据可供读取
writefds中列出的文件描述符被监视是否有写入操作
execptfds中列出的文件描述符则被监视是否发生异常
(这些状态应用于套接字)

过程:
select调用前需要 FD_ZERO(&readfds)// 清除描述词组set的全部位
FD_SET()//设置描述词组set中的相关位

当调用select()时,由内核根据IO状态修改fd_set的内容
select对应于内核中的sys_select调用,
sys_select首先将第二,三,四个参数指向的fd_set拷贝到内核,然后对每个被set的描述符进行查询,

并且记录在临时结果fd_set中,如果有事件发生,select将临时结果写到用户空间并返回;
当轮询一遍后没有任何事件发生,如果指定了超时时间,那么select会睡眠到超时。

select返回成功,则每组set都被修改以使它只包含准备好的I/O文件描述符

select返回后,需要逐一检查关注的描述符是否被SET(事件是否发生)。(FD_ISSET)

 

传统的select/poll的一个致命的弱点是当你拥有一个很大的socket集合,不过由于网络延时,任一时间

只有部分的socket是活跃的,但是select/poll每次调用都会线性扫描全部的集合,导致效率线性下降,

epoll就是为了解决这个问题而存在的

epoll机制
epoll只会对活跃的socket进行操作,这是因为在内核实现中epoll是根据每个fd上面的callback函数实现

的,那么只有活跃的socket才会主动的去调用callback函数,
使用mmap加速了内核与用户空间的消息传递(mmap将一个文件或者其它对象映射进内存)避免了select,

poll的内存拷贝。

2 具体的比较
select:
select本质上是通过设置或检查存放fd标志位的数据结构来进行下一步处理,这样所带来的缺点是:
1 单个进程可监视的fd数量被限制
2 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
3 对socket进行扫描是线性扫描
poll:
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,
如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起

当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd,这个过程经历了多次无谓的遍历。
1 它没有最大连接数的限制,原因是它是基于链表存储的。
2 大量fd的数组被整体复制于用户态和内核地址空间之间
3 poll有一个特点,水平触发,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd
epoll:
1 epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就绪态,并

且只会通知一次
2 epoll使用mmap减少复制开销
3 epoll使用事件的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的

回调机制来激活该fd,epoll_wait便可以收到通知

分类比较:
   1 一个进程支持的所能打开的最大连接数
select:单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器

上,大小就是32*32,同理64位机器上FD_SETSIZE为32*64)

poll:poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的

epoll:虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打

开20万左右的连接

   2 FD剧增后带来的IO效率问题
1 select
 因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题

”。
2 poll同select
3 因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用

callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有

socket都很活跃的情况下,可能会有性能问题。
    3 消息传递方式:
select
 内核需要将消息传递到用户空间,都需要内核拷贝动作
 
poll
 同上
 
epoll
 epoll通过内核和用户空间共享一块内存来实现的。
 

 总结如下:

区别(epoll相对select优点)主要有三:

1.select的句柄数目受限,在linux/posix_types.h头文件有这样的声明:#define __FD_SETSIZE    1024  表示select最多同时监听1024个fd。而epoll没有,它的限制是最大的打开文件句柄数目。

2.epoll的最大好处是不会随着FD的数目增长而降低效率,在selec中采用轮询处理,其中的数据结构类似一个数组的数据结构,而epoll是维护一个队列,直接看队列是不是空就可以了。epoll只会对"活跃"的socket进行操作---这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数(把这个句柄加入队列),其他idle状态句柄则不会,在这点上,epoll实现了一个"伪"AIO。但是如果绝大部分的I/O都是“活跃的”,每个I/O端口使用率很高的话,epoll效率不一定比select高(可能是要维护队列复杂)。

3.使用mmap加速内核与用户空间的消息传递。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核于用户空间mmap同一块内存实现的。

 


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值