(1)select
select最早于1983年出如今4.2BSD中,它通过一个select()系统调用来监视多个文件描写叙述符的数组。当select()返回后,该数组中就绪的文件描写叙述符便会被内核改动标志位。使得进程能够获得这些文件描写叙述符从而进行兴许的读写操作。
select眼下差点儿在全部的平台上支持,其良好跨平台支持也是它的一个长处,其实从如今看来。这也是它所剩不多的长处之中的一个。
select的一个缺点在于单个进程能够监视的文件描写叙述符的数量存在最大限制,在Linux上一般为1024,只是能够通过改动宏定义甚至又一次编译内核的方式提升这一限制。另外。select()所维护的存储大量文件描写叙述符的数据结构,随着文件描写叙述符数量的增大,其复制的开销也线性增长。同一时候。由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()会对全部socket进行一次线性扫描,所以这也浪费了一定的开销。
(2)poll
poll在1986年诞生于System V Release 3,它和select在本质上没有多大区别,可是poll没有最大文件描写叙述符数量的限制。
poll和select相同存在一个缺点就是,包括大量文件描写叙述符的数组被总体复制于用户态和内核的地址空间之间,而不论这些文件描写叙述符是否就绪,它的开销随着文件描写叙述符数量的添加而线性增大。另外,select()和poll()将就绪的文件描写叙述符告诉进程后,假设进程没有对其进行IO操作,那么下次调用select()和poll()的时候将再次报告这些文件描写叙述符。所以它们一般不会丢失就绪的消息,这样的方式称为水平触发(Level Triggered)。(3)epoll
直到Linux2.6才出现了由内核直接支持的实现方法,那就是epoll。它差点儿具备了之前所说的一切长处,被公觉得Linux2.6下性能最好的多路I/O就绪通知方法。
epoll能够同一时候支持水平触发和边缘触发(Edge Triggered。仅仅告诉进程哪些文件描写叙述符刚刚变为就绪状态,它仅仅说一遍。假设我们没有採取行动,那么它将不会再次告知,这样的方式称为边缘触发)。理论上边缘触发的性能要更高一些,可是代码实现相当复杂。
epoll相同仅仅告知那些就绪的文件描写叙述符。并且当我们调用epoll_wait()获得就绪文件描写叙述符时,返回的不是实际的描写叙述符。而是一个代表就绪描写叙述符数量的值,你仅仅须要去epoll指定的一个数组中依次取得相应数量的文件描写叙述符就可以。这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描写叙述符在系统调用时复制的开销。
另一个本质的改进在于epoll採用基于事件的就绪通知方式。

在select/poll中,进程仅仅有在调用一定的方法后。内核才对全部监视的文件描写叙述符进行扫描,而epoll事先通过epoll_ctl()来注冊一个文件描写叙述符,一旦基于某个文件描写叙述符就绪时,内核会採用相似callback的回调机制,迅速激活这个文件描写叙述符,当进程调用epoll_wait()时便得到通知。

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便能够收到通知

注:水平触发(level-triggered):仅仅要满足条件,就触发一个事件(仅仅要有数据没有被获取。内核就不断通知你);边缘触发(edge-triggered)——每当状态变化时。触发一个事件。

Select

Poll

Epoll

支持最大连接数

1024(x86) or 2048(x64)

无上限

无上限

IO效率

每次调用进行线性遍历,时间复杂度为O(N)

每次调用进行线性遍历,时间复杂度为O(N)

使用“事件”通知方式。每当fd就绪。系统注冊的回调函数就会被调用,将就绪fd放到rdllist里面。这样epoll_wait返回的时候我们就拿到了就绪的fd。

时间发复杂度O(1)

fd拷贝

每次select都拷贝

每次poll都拷贝

调用epoll_ctl时拷贝进内核并由内核保存。之后每次epoll_wait不拷贝

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

select

由于每次调用时都会对连接进行线性遍历。所以随着FD的添加会造成遍历速度慢的“线性下降性能问题”。

poll

同上

epoll

由于epoll内核中实现是依据每一个fd上的callback函数来实现的。仅仅有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下。使用epoll没有前面两者的线性下降的性能问题,可是全部socket都非常活跃的情况下,可能会有性能问题。

消息传递方式

select

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

poll

同上

epoll

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

使用:

当同一时候须要保持非常多的长连接,并且连接的开关非常频繁时,就能够发挥epoll最大的优势了。这里与server模型其实已经有些交集了。
同一时候须要保持非常多的长连接,并且连接的开关非常频繁,最高效的模型是非堵塞、异步IO模型。并且不要用select/poll。这两个API的有着O(N)的时间复杂度。
在Linux用epoll,BSD用kqueue。Windows用IOCP。或者用Libevent封装的统一接口(对于不同平台Libevent实现时採用各个平台特有的API)。这些平台特有的API时间复杂度为O(1)。 然而在非堵塞,异步I/O模型下的编程是非常痛苦的。由于I/O操作不再堵塞,报文的解析须要小心翼翼。并且须要亲自管理维护每一个链接的状态。并且为了充分利用CPU,还应结合线程池,避免在轮询线程中处理业务逻辑。
但这样的模型的效率是极高的。以知名的httpservernginx为例,能够轻松应付上千万的空连接+少量活动链接。每一个连接连接仅须要几K的内核缓冲区,想要应付很多其它的空连接,仅仅需简单的添加内存(数据来源为淘宝一位project师的一次技术讲座,并未实測)。

这使得DDoS攻击者的成本大大添加,这样的模型攻击者仅仅能将server的带宽全部占用。才干达到目的,而双方的投入是不成比例的。

注:长连接——连接后始终不断开,然后进行报文发送和接受;短链接——每一次通讯都建立连接,通讯完毕即断开连接,下次通讯再建立连接。

linux提供了select、poll、epoll接口来实现IO复用,三者的原型例如以下所看到的:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

select、poll、epoll_wait參数及实现对照
1. select的第一个參数nfds为fdset集合中最大描写叙述符值加1,fdset是一个位数组,其限制大小为__FD_SETSIZE(1024),位数组的每一位代表其相应的描写叙述符是否须要被检查。select的第二三四个參数表示须要关注读、写、错误事件的文件描写叙述符位数组,这些參数既是输入參数也是输出參数,可能会被内核改动用于标示哪些描写叙述符上发生了关注的事件。

所以每次调用select前都须要又一次初始化fdset。

timeout參数为超时时间,该结构会被内核改动,其值为超时剩余的时间。
select相应于内核中的sys_select调用。sys_select首先将第二三四个參数指向的fd_set复制到内核,然后对每一个被SET的描写叙述符调用进行poll,并记录在暂时结果中(fdset),假设有事件发生,select会将暂时结果写到用户空间并返回;当轮询一遍后没有不论什么事件发生时,假设指定了超时时间,则select会睡眠到超时。睡眠结束后再进行一次轮询,并将暂时结果写到用户空间。然后返回。
select返回后。须要逐一检查关注的描写叙述符是否被SET(事件是否发生)。

2. poll与select不同,通过一个pollfd数组向内核传递须要关注的事件,故没有描写叙述符个数的限制,pollfd中的events字段和revents分别用于标示关注的事件和发生的事件,故pollfd数组仅仅须要被初始化一次。
poll的实现机制与select相似,其相应内核中的sys_poll,仅仅只是poll向内核传递pollfd数组,然后对pollfd中的每一个描写叙述符进行poll,相比处理fdset来说。poll效率更高。
poll返回后。须要对pollfd中的每一个元素检查其revents值,来得指事件是否发生。

3. epoll通过epoll_create创建一个用于epoll轮询的描写叙述符,通过epoll_ctl加入/改动/删除事件,通过epoll_wait检查事件,epoll_wait的第二个參数用于存放结果。
epoll与select、poll不同,首先,其不用每次调用都向内核拷贝事件描写叙述信息,在第一次调用后。事件信息就会与相应的epoll描写叙述符关联起来。

另外epoll不是通过轮询。而是通过在等待的描写叙述符上注冊回调函数。当事件发生时,回调函数负责把发生的事件存储在就绪事件链表中,最后写到用户空间。
epoll返回后,该參数指向的缓冲区中即为发生的事件,对缓冲区中每一个元素进行处理就可以,而不须要像poll、select那样进行轮询检查。

Select、Poll与Epoll比較的更多相关文章

  1. Linux下select, poll和epoll IO模型的详解

    http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll 介绍 Epoll 可是当前在 Linux 下开发大规模并发网络程序的热 ...

  2. I/O复用中的 select poll 和 epoll

    I/O复用中的 select poll 和 epoll: 这里有一些不错的资料: I/O多路复用技术之select模型: http://blog.csdn.net/nk_test/article/de ...

  3. (转)Linux下select, poll和epoll IO模型的详解

    Linux下select, poll和epoll IO模型的详解 原文:http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll ...

  4. linux select poll and epoll

    这里以socket文件来阐述它们之间的区别,假设现在服务器端有100 000个连接,即已经创建了100 000个socket. 1 select和poll 在我们的线程中,我们会弄一个死循环,在循环里 ...

  5. Select,poll,epoll复用

    Select,poll,epoll复用 1)select模块以列表的形式接受四个参数,分别是可读对象,可写对象,产生异常的对象,和超时设置.当监控符对象发生变化时,select会返回发生变化的对象列表 ...

  6. 聊聊select, poll 和 epoll

    聊聊select, poll 和 epoll 假设项目上需要实现一个TCP的客户端和服务器从而进行跨机器的数据收发,我们很可能翻阅一些资料,然后写出如下的代码. 服务端 void func(int s ...

  7. [转载] select, poll和epoll的区别

    源地址:http://sheepxxyz.blog.163.com/blog/static/61116213201022003513530/ 随着2.6内核对epoll的完全支持,网络上很多的文章和示 ...

  8. Linux中select poll和epoll的区别

    在Linux Socket服务器短编程时,为了处理大量客户的连接请求,需要使用非阻塞I/O和复用,select.poll和epoll是Linux API提供的I/O复用方式,自从Linux 2.6中加 ...

  9. Linux select/poll和epoll实现机制对比

    关于这个话题,网上已经介绍的比较多,这里只是以流程图形式做一个简单明了的对比,方便区分. 一.select/poll实现机制 特点: 1.select/poll每次都需要重复传递全部的监听fd进来,涉 ...

随机推荐

  1. (转)js函数前加分号和感叹号是什么意思?有什么用?

    转载地址:https://www.cnblogs.com/mq0036/p/4605255.html 一般看JQuery插件里的写法是这样的 (function($) { //... })(jQuer ...

  2. laravel5.3之后可以使用withCount()这个方法

    比如:文章控制器ArticleController.php查询文章列表数据的时候用withCount连接Comment,Zan模型直接统计每篇文章的评论和点赞数量. 使用之前需要在文章模型文件Arti ...

  3. IdentityServer4结合AspNetCore.Identity实现登录认证踩坑填坑记录

    也可以自定义实现,不使用IdentityServer4.AspNetIdentity这个包,当然还要实现其他接口IResourceOwnerPasswordValidator. IProfileSer ...

  4. [转] React 最佳实践——那些 React 没告诉你但很重要的事

    前言:对很多 react 新手来说,网上能找到的资源大都是些简单的 tutorial ,它们能教会你如何使用 react ,但并不会告诉你怎么在实际项目中优雅的组织和编写 react 代码.用谷歌搜中 ...

  5. CCF计算机职业资格认证考试题解

    CCF计算机职业资格认证考试题解系列文章为meelo原创,请务必以链接形式注明本文地址 CCF计算机职业资格认证考试题解 CCF计算机软件能力认证(简称CCF CSP认证)是CCF计算机职业资格认证系 ...

  6. linux系统下创建lvm挂载到指定目录

    1 .背景 在企业中有时我们为方便安装软件.数据的管理,需要把安装软件.数据放到固定目录下,磁盘满了方便扩展,这里假如需要一个/data目录存放数据,并单独进行挂载. 2.操作步骤 2.1  划分磁盘 ...

  7. 主动学习——active learning

    阅读目录 1. 写在前面 2. 什么是active learning? 3. active learning的基本思想 4. active learning与半监督学习的不同 5. 参考文献   1. ...

  8. android 静音

    语音朗读 静音,设置变量识别好了. android插件下载开发. 实际可以这样, 保存应用的图标,名称,包名,下载地址. 插件作为apk.  或许希望apk不在桌面有图标.也可以.但是也没必要. 当下 ...

  9. [CEOI2018]Global warming

    [CEOI2018]Global warming 题目大意: 给定\(n(n\le2\times10^5)\),你可以将任意\(a_{l\sim r}(1\le l\le r\le n)\)每一个元素 ...

  10. Codeforces Round #373 (Div. 2) A. Vitya in the Countryside 水题

    A. Vitya in the Countryside 题目连接: http://codeforces.com/contest/719/problem/A Description Every summ ...