1.1 select (简略)
1.2 poll (简略)
1.3 epoll (详细)
2 select,poll,epoll对比简单结论
epoll与selec、poll的实现机制上有着根本的不同,使用方式上也差别很大。select和poll都是主动轮询机制,需要遍历每一个描述符。epoll是被动触发方式,基于每个描述的callback机制实现。另外epoll通过mmap在内核和用户空间共享同一块内存,而poll和select机制需要内核及用户空间之间的拷贝动作来传递消息。因此,一般网络应用情况下(大量链接描述符,但仅有少部分活跃)epoll比poll和select性能上有着质的提升,select和poll需要遍历每一个描述符,性能是线性下降的。而epoll机制只有活跃的描述符才会通过callback有相应的触发。但特殊情况下(监听的描述符很少,且都很活跃),这时select和poll效率要更高,因为epoll多层函数回调的成本会显得比较高,而且所有活跃描述符都会进行callback。 补充说明:三种机制的核心都是通过相应描述符的等待进程队列来实现的,只不过等待队列的唤醒机制不同,一个主动轮询、一个被动触发。另外,在发生event的描述符鉴别上,select和poll只知道有几个描述发生了事件,但不知道是具体哪几个仍需要,for(n_all)通过判断标志位来找出具体哪几个有事件发生。而epoll会直接返回相应数目的发生事件的描述符for(n_occured)。这里可见select和poll不仅在内核实现中需要定时轮询遍历所有描述符对应设备,而且用户应用程序设计中,也需要遍历整个描述符集合找出相应的若干个发生事件的描述。
时间发生后处理流程对比:
select直到找到n个后退出循环,实际循环次数可能远大于n。(或者干脆循环num of fd次)
int n = select(&readset,NULL,NULL,100);
for (int i = 0; n > 0; ++i)
{
if (FD_ISSET(fdarray[i], &readset))
{
do_something(fdarray[i]);
--n;
}
}
n=epoll_wait(epfd,events,20,500);
for(i=0;i
{
do_something(events[n]);
}
3 select,poll,epoll详细对比
3.1 基本实现
select |
select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:
1 单个进程可监视的fd数量被限制 2 需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大 3 对socket进行扫描时是线性扫描 |
poll |
poll 本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍 历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。 它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。 poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。 |
epoll |
epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就需态,并且只会通知一次。
在前面说到的复制问题上,epoll使用mmap减少复制开销。 还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知 |
3.2 支持一个进程所能打开的最大连接数
select | 单 个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是32*32,同理64位机器上 FD_SETSIZE为32*64),当然我们可以对它进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。 |
poll | poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的 |
epoll |
虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接。 |
3.3 FD剧增后带来的IO效率问题
select |
因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。 |
poll | 同上 |
epoll | 因 为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket 较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。 |
3.4 消息传递方式
select | 内核需要将消息传递到用户空间,都需要内核拷贝动作 |
poll | 同上 |
epoll | epoll通过内核和用户空间共享一块内存来实现的 |
4 参考内容
http://xingyunbaijunwei.blog.163.com/blog/static/76538067201241685556302/
http://blog.csdn.net/tianmohust/article/details/6677985
http://blog.csdn.net/zhaozhanyong/article/details/5410887
http://blog.csdn.net/hairetz/article/details/6337817
http://blog.csdn.net/hairetz/article/details/6337817