一:多路复用的举例

以一个生活中的例子来解释:

假设你在大学中读书,要等待一个朋友(数据)来访(要读),而这个朋友只知道你在A号楼(socket集合),但是不知道你具体住在哪里,于是你们约好了在A号楼门口见面。

如果你使用的阻塞IO模型来处理这个问题,那么你就只能一直守候在A号楼门口等待朋友的到来,在这段时间里你不能做别的事情,不难知道,这种方式的效率是低下的。

现在时代变化了,开始使用多路复用IO模型来处理这个问题。你告诉你的朋友来了A号楼找楼管大妈,让她告诉你该怎么走。这里的楼管大妈扮演的就是多路复用IO的角色(select,poll,epoll)。

select版大妈做的是如下的事情:比如同学甲的朋友来了,select版大妈比较笨,她带着朋友挨个房间进行查询谁是同学甲,你等的朋友来了。在实际的代码中,select版大妈做的是以下的事情:

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;
}
}

epoll版大妈就比较先进了:她记下了同学甲的信息,比如说他的房间号(socket fd),那么等同学甲的朋友到来时,只需要告诉该朋友同学甲在哪个房间即可,不用自己亲自带着人满大楼的找人了。epoll版大妈做的事情可以用如下的代码表示:

n = epoll_wait(epfd,events,20,500);
for(i=0;i<n;++i)
{
do_something(events[n]);
}

在epoll中,关键的数据结构epoll_event定义如下:

typedef union epoll_data
{
void *ptr;
int fd;
__uint32_t u32;
__uint64_t u64;
} epoll_data_t; struct epoll_event
{
__uint32_t events; /* Epoll events */
epoll_data_t data; /* User data variable */
};

可以看到,epoll_data是一个union结构体,它就是epoll版大妈用于保存同学信息的结构体,它可以保存很多类型的信息:fd,指针等等。有了这个结构体,epoll大妈可以不用吹灰之力就可以定位到同学甲。

别小看了这些效率的提高,在一个大规模并发的服务器中,轮询IO是最耗时间的操作之一。再回到那个例子中,如果每到来一个朋友楼管大妈都要全楼的查询同学,那么处理的效率必然就低下了,过不久楼底就有不少的人了。

二:select和poll

尽管poll和select完成一样的工作,但是poll仍然优于select:

1:poll无需使用者计算最大的文件描述符值加一和传递该参数。

2:poll在应对较大值的文件描述符时更具效率。想象一下select监视值为900的文件描述符时的情况,内核需要检查每个集合中的每个比特位,直到第900个。

3:select的文件描述符集合是静态大小的,所以要做出权衡:要么集合很小,限制了select可以监视的文件描述符的最大值,要么很大,但是效率不高。尤其是当不能确定集合的组成是否稀疏时,对较大掩码的操作效率不高。而用poll则可以创建合适大小的数组。

4:若用select,文件描述符集合会在返回时重新创建,这样的话,之后每个调用都必须重新初始化它们。poll调用分离了输入(events)和输出(revents),数组无需改变即可重用。

select的参数类型fd_set没有将文件描述符和事件绑定,它仅仅是一个文件描述符集合。内核对fd_set集合的修改,使应用程序在下次调用select的时候,需要重置3个fd_set集合。

poll的参数pollfd要比select聪明一些,它把文件描述符和事件都定义在其中,内核每次修改的是pollfd结构体中的revents成员,而events成员保持不变,因此下次调用poll的时候,无需重置。

5:select的timeout参数在返回时是未定义的。

但是select系统调用也有几个不错的地方:

1:某些系统不支持poll,所以select的可移植性更好。

2:select提供了更好的超时方案:直到微妙级。

三:epoll

select和poll都只能工作在相对低效的LT模式,而epoll可以工作在ET模式,而且还支持EPOLLONESHOT事件,该事件可以进一步减少可读、可写和异常事件触发的次数。

从内核实现原理上来说,select和poll采用的都是轮询的方式,即每次调用都要扫描整个注册文件描述符集合,并将其中就绪的文件描述符返回给用户程序,因此它们检测就绪事件的时间复杂度是O(n)。

epoll_wait则不同,它采用的是回调的方式。内核检测到就绪的文件描述符时,将触发回调函数,回调函数就将该文件描述符上对应的事件插人内核就绪事件队列。内核最后在适当的时机将该就绪事件队列中的内容拷贝到用户空间。因此epoll_wait无须轮询整个文件描述符集合来检洲哪些事件已经就绪,其算法时问复杂度是0(1)。

但是。当活动连接比较多的时候。epoll_wait的效率未必比就select和poll高,因为此时回调函数被触发得过于频繁。所以epoll_wait适用于连接数量多,但活动连接较少的情况。

从应用程序的角度看,每次select和poll调用,都返回整个用户注册的事件集合,包括就绪的和未就绪的,所以应用程序寻找就绪文件描述符的时间是O(n)。

epoll采用完全不同的方式来管理用户注册的事件,它在内核中维护事件表,提供独立的系统调用epoll_ctl进行添加,删除,修改事件。这样,每次epoll_wait调用都直接从内核事件表中取得用户注册的事件,而无需反复从用户空间读入这些事件,而且,epoll_wait只返回就绪事件,所以,应用程序寻找就绪文件描述符的时间为O(1)。

四:其他

当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,告诉内核,如果这个描述符的中断到了,就把它放到准备就绪list链表里。所以,当一个socket上有数据到了,内核在把网卡上的数据copy到内核中后,然后把socket插入到准备就绪链表里。

如此,一颗红黑树,一张准备就绪句柄链表,少量的内核cache,就帮我们解决了大并发下的socket处理问题。执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。

epoll简介(二)的更多相关文章

  1. Epoll简介以及例子

    第一部分:Epoll简介 问题 :  Select,Poll和Epoll的区别 答案 : Epoll和Select的区别 1. 遍历方式的区别.select判断是否有事件发生是遍历的,而epoll是事 ...

  2. {Django基础七之Ajax} 一 Ajax简介 二 Ajax使用 三 Ajax请求设置csrf_token 四 关于json 五 补充一个SweetAlert插件(了解)

    Django基础七之Ajax 本节目录 一 Ajax简介 二 Ajax使用 三 Ajax请求设置csrf_token 四 关于json 五 补充一个SweetAlert插件(了解) 一 Ajax简介 ...

  3. Windbg 脚本命令简介 二, Windbg command

    Windbg  脚本命令简介 二, Windbg  script command $<, $><, $$<, $$><, $$>a< (Run Scri ...

  4. epoll简介

    1.epoll简介 epoll是I/O事件通知工具,与select/poll相比,epoll最大的好处在于它不会随着监听fd数目的增长而效率降低.epoll API既可以用作edge触发的接口,也可以 ...

  5. {Django基础七之Ajax} 一 Ajax简介 二 Ajax使用 三 Ajax请求设置csrf_token 四 关于json 五 补充一个SweetAlert插件(了解)

    {Django基础七之Ajax} 一 Ajax简介 二 Ajax使用 三 Ajax请求设置csrf_token 四 关于json 五 补充一个SweetAlert插件(了解)   Django基础七之 ...

  6. 基本I/O模型与Epoll简介

    5种基本的I/O模型:1)阻塞I/O ;2)非阻塞I/O; 3)I/O复用(select和poll);4)信号驱动I/O(SIGIO);5)异步I/O(POSIX.1的aio_系列函数). 操作系统中 ...

  7. epoll简介 与 UDP server的实现

    Abstractepoll是Linux内核为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系 ...

  8. epoll简介(一)

    一:概述   1:简介 EPOLL类似于POLL,是Linux特有的一种IO多路复用的机制.它在2.5.44内核中引入. 对于大量的描述符处理,EPOLL更有优势,它提供了三个系统调用来创建管理epo ...

  9. WPF Binding值转换器ValueConverter使用简介(二)-IMultiValueConverter

    注: 需要继承IMultiValueConverter接口,接口使用和IValueConverter逻辑相同. 一.MultiBinding+Converter 多值绑定及多值转换实例 当纵向流量大于 ...

随机推荐

  1. 关于python 环境变量

    1.默认命令行的启动的python 版本,这依赖于系统的环境变量. 见上一篇关于linux 环境变量的PATH 变量的设置 2.python 中 import 包的搜索路径, 即除了当前程序目录,能i ...

  2. GIT → 10:基于IntelliJ IDEA的Git 操作

    GIT → 10:基于IntelliJ IDEA的Git 操作

  3. linux如何将分组权限置为空

    两种方法 方法一:使用-符号 chmod g=- monkey.py#可以单独指定一个 方法二:简写方式,用0表示 chmod 740 monkey.py#必须同时指定三个的权限

  4. 阿里云SaaS生态战略发布:成就亿级营收独角兽

    导语:本文中,阿里云智能资深技术专家黄省江从“势”“道”“术”三个方面分享了自己对于SaaS生态的理解,并介绍了SaaS加速器发布以来在产品.技术和商业侧最新的一些进展. 在321北京峰会上,阿里云公 ...

  5. OpenSmtp 发送邮件

    1.采用发送一个简单邮件 示例: private int smtpPort; private string smtpHost; private int recieveTimeout; private ...

  6. 贝叶斯--旧金山犯罪分类预测和电影评价好坏 demo

    来源引用:https://blog.csdn.net/han_xiaoyang/article/details/50629608 1.引言 贝叶斯是经典的机器学习算法,朴素贝叶斯经常运用于机器学习的案 ...

  7. 使用FastJson对实体类和Json还有JSONObject之间的转换

    1. 实体类或集合转JSON串 String jsonString = JSONObject.toJSONString(实体类); 2.JSON串转JSONObject JSONObject json ...

  8. 地不安装Oracle,plsql远程连接数据库

    由于Oracle的庞大,有时候我们需要在只安装Oracle客户端如plsql.toad等的情况下去连接远程数据库,可是没有安装Oracle就没有一切的配置文件去支持.最后终于发现一个很有效的方法,Or ...

  9. python 匹配集合与补集

  10. small标签

    <small> 标签将旁注 (side comments) 呈现为小型文本. 免责声明.注意事项.法律限制或版权声明的特征通常都是小型文本.小型文本有时也用于新闻来源.许可要求. 对于由 ...