什么是epoll

epoll 与 select,poll 一样,其本质目的都是为了实现IO多路复用,将对多个文件描述符的等待时间重叠,提高 IO 的效率。但 epoll 不同的是,epoll 几乎解决了 select 和 poll 的所有缺点,具备之前所说的一切优点。

为什么要有epoll

虽然 poll 的存在解决了使用 select 太复杂的问题以及 poll 并没有文件描述符最大数量的限制。但 poll 并没有解决 select 本质上的不足。poll 与 select 一样,存在需要系统轮询遍历和大量内核态与用户态来回拷贝的问题。

epoll 是 eventpoll,它在本质上解决了上述的问题,进一步提高了 IO 的效率。

epoll相关函数介绍

epoll 存在3个相关的函数,虽然在个数上相较于 select,poll 更多了,但使用起来,其实更方便了。

epoll_create

要使用 epoll 时,首先调用 epoll_create() ,目的是创建一个 epoll 实例,为后续的操作做铺垫。

int epoll_create(int size);

● 这里的参数 size 在实际使用当中是被忽略的。

● 函数的返回值是一个文件描述符,需要通过该文件描述符完成后续操作。

● 在使用完后,需要调用 close() 关闭。

epoll_ctl

epoll 的事件注册函数,它有三个作用:

  1. 向内核中注册要监听的文件描述符和关心的事件;

  2. 修改已注册文件描述符所关心的事件;

  3. 删除一个已注册的文件描述符;

int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);

● epfd:表示已经创建的 epoll 实例,也就是 epoll_create() 的返回值;

● op:表示所进行的操作,用三个宏表示;

EPOLL_CTL_ADD:注册新的 fd 到 epfd 中;

EPOLL_CTL_MOD:修改已经注册的 fd 中所关心的事件;

EPOLL_CTL_DEL:删除一个已经注册的 fd;

● fd:需要监听的文件描述符;

● event:表示需要内核关心的事件;

struct epoll_event 的结构如下:

events 可以是以下几个宏的集合

● EPOLLIN:表示对应的文件描述符可以读(包括对端SOCKET正常关闭);

● EPOLLOUT:表示对应的文件描述符可以写;

● EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);

● EPOLLERR:表示对应的文件描述符发生错误;

● EPOLLHUP:表示对应的文件描述符被挂断;

● EPOLLET:将EPOLL设为边缘触发(Edge Triggered)模式这是相对于水平触发(Level Triggered)来说的。

● EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL实例里。

epoll_wait

用于获取已经就绪的事件。

int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);

● events:在用户层就需要提前分配好 epoll_event 结构体数组,epoll 会把已经就绪的事件拷贝到这个数组中;

● maxevents:events 数组所能存放的最大元素个数,这个值不能大于调用 epoll_create() 中的 size;

● timeout:超时时间,单位是毫秒,填 0 是非阻塞,填 -1 是阻塞,填具体值则是超时时间;

epoll的工作原理

epoll 是如何解决 select 和 poll 存在的问题的呢?其实主要在于三个方面,红黑树 + 链表 + 回调函数。

当一个进程调用 epoll_create 函数时,内核会创建一个 eventpoll 的结构体,而这个结构体中有两个成员与 epoll 的使用密切相关,一个是红黑树的根节点,一个是链表的头节点。

struct eventpoll
{...//红黑树的根节点,这颗红黑树中存放着所有需要监听的事件struct rb_root rbr;//链表的头节点,链表中存放着已经就绪的事件,通过 epoll_wait 返回给用户struct list_head rdlist;...
}

内核会对每一个事件构建一个 epitem 的结构体,红黑树与链表中的节点就是基于这个 epitem 来构建的。

struct epitem
{...struct rb_node rbn;        //红黑树节点struct list_head rdllink;  //链表节点struct epoll_filefd ffd;   //事件句柄信息struct eventpoll* ep;      //回指向所属的eventpoll对象struct epoll_event event;  //关系的事件...
}

使用 epoll 的步骤如下:

● 首先调用 epoll_create 创建 epoll 实例。每个 epoll 实例都有一个独立的 eventpoll 结构体;

● 然后调用 epoll_ctl ,将需要监听的文件描述符进行注册,内核会为这次动作构建一个红黑树的节点,并插入到红黑树中;

● 之后,内核会为这个事件与网卡驱动程序建立回调关系,当事件就绪时,会调用已经建立好的回调方法。这个回调方法会将发生的事件添加到链表中;

● 最后调用 epoll_wait ,内核先检查这个链表是否为空,若不为空则直接将链表中的数据拷贝到用户层的 events 数组中,而这个操作的时间复杂度是 O(1)。

epoll的优点

接口使用方便,含义清晰明了。使用时,不需要每次都循环设置关注的文件描述符,而且将关心事件与就绪事件分开了,做到了输入输出参数分离;

数据拷贝更轻量化。epoll 会在使用 EPOLL_CTL_ADD 时将文件描述符拷贝到内核的红黑树,以及将少量的就绪事件拷贝到用户层的 events 数组。不需要每次将所有关心的文件描述符拷贝到内核与从内核拷贝到用户;

使用了回调机制。避免了内核每次轮询遍历要关心的文件描述符,而是文件描述符就绪时,将其添加到链表中。并且这个操作的时间复杂度是 O(1),即使文件描述符很多,而就绪的仍是少数;

文件描述符没有数量限制。

epoll的工作方式

epoll 存在两种工作模式,一种是 LT (Level Triggered) —— 水平触发;一种是 ET (Edge Triggered) —— 边缘触发

select,poll 的工作模式只有 LT,而 LT 是 epoll 的默认工作模式。如果要将 epoll 设置成 ET 模式,则需要调用 epoll_ctl 函数,为某一个文件描述符,在关心的事件 events 中添加上 EPOLLET。当然,epoll 的工作模式是相较于其监听的一个文件描述符来说的。


先通过一个例子来理解下这两种工作模式。

假如现在有一家菜鸟驿站,这家驿站中有两名员工,一个名叫张三,他是一个很负责的人;一个名叫李四,他是一个很懒的人;而你呢,由于之前在网上买的东西有急用,而且同时买了几件;你在这一天内,会多次进入驿站,并询问当天的工作人员,是否有你的快递;

假如这天上班的是张三,张三比较负责,会严格记录当前时刻存在快递的人。

你进入驿站,询问张三:“目前有我的快递吗?”

张三说:“是的,有你的。”

这时候你拿走了一部分快递,然而你并没有拿完。

过了一会,你又到了驿站,并询问张三,说:“目前有我的快递吗?”

张三说:“是的,有你的。”

这时候,你还是之拿走了一部分。

......

一段时间之后,你终于把快递拿完了。


假如今天上班的是李四,李四比较懒,只会在快递到来的时候记录收件人的名字。就算一次到来多个快递,你没拿完,李四也会认为你拿完了,并将你的名字去除。

当你第一次进入驿站,询问李四:“目前有我的快递吗?”

李四说:“是的,有你的。”

但是,你这次并没有拿完所有的快递。

当你之后再进入到驿站时,询问李四:“目前是否有我的快递?”

李四回答说:“抱歉,当前并没有你的快递。”

情况一:

如果之后,你又有新的快递到了,李四则会将你的名字记录上,并在你下次到来时告知你。

情况二:

如果之后,再没有你的新快递到达,李四则会一直告诉你,没有你的快递。

并且对于之后你再到达驿站询问李四,李四的回答都是目前没有你的快递。

而你对工作人员是百分百信任的,当他对你说NO的时候,你都会离开驿站...

这时候,就发生了一件错误的事,你永远也拿不到之前没拿完,剩余的快递了。


在上面的例子中,张三的工作模式就是 LT ,李四的工作模式就是 ET。

本质上,LT 工作模式下,只要文件描述符上有数据,文件描述符就会一直就绪;而 ET 工作模式下,只有当一个文件描述符的数据增多时,才会就绪;

ET模式和非阻塞文件描述符

使用 ET 模式的 epoll ,需要将文件描述符设置为非阻塞。

● 假设文件描述符为阻塞,当我们需要读取数据时,由于是循环读取,当读到最后一次,没有数据可读的时候,就会被阻塞住;

● 而我们需要的是,有数据就读取;没有数据时,就直接返回。

● 所以需要将文件描述符设置为非阻塞;

LT和ET的对比

目前看来,LT 是一种比 ET 更靠谱的工作模式,在 LT 模式下,就算分多次拿取数据,你也一定能拿完所有的数据;而在 ET 模式下,一旦第一次没拿完所有的数据,之后就拿不到剩余的数据了。

但实际上,在大多数情况下,ET 的效率是要高于 LT 的。因为在 ET 模式下,一次没拿完数据,之后的数据便不再提示上层拿取。这样做的好处是,在一次事件就绪时,倒逼着程序员在本次就将所有数据处理完,而不能一次只处理一部分数据。

这样一来,由于不会调用多次 epoll_wait 去处理相同事件的部分数据,使得 epoll_wait 的调用次数就减少了。

但是,如果在 LT 模式下也能做到一次将数据处理完,其实性能也是一样的。

另外,ET 下的代码复杂度更高了。

多路复用之——epoll相关推荐

  1. Linux 下 I/O 多路复用技术 epoll

    Linux 下 I/O 多路复用技术 epoll 流?I/O操作?阻塞? 解决阻塞死等待的方法 方法一:阻塞 + 多进程 / 多线程. 方法二:非阻塞 + 忙轮询. 方法三:select(与平台无关) ...

  2. IO多路复用之epoll总结 http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html

    IO多路复用之epoll总结 http://www.cnblogs.com/Anker/archive/2013/08/17/3263780.html

  3. linux IO多路复用 select epoll

    概念 IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程 通俗理解(摘自网上一大神) 这些名词比较绕口,理解涵义就好.一个epoll场景:一个酒吧服务员(一个线程),前 ...

  4. IO多路复用之epoll总结

    1.基本知识 epoll是在2.6内核中提出的,是之前的select和poll的增强版本.相对于select和poll来说,epoll更加灵活,没有描述符限制.epoll使用一个文件描述符管理多个描述 ...

  5. python网络编程——IO多路复用之epoll

    什么是epoll epoll是什么?在linux的网络编程中,很长的时间都在使用select来做事件触发.在linux新的内核中,有了一种替换它的机制,就是epoll.当然,这不是2.6内核才有的,它 ...

  6. 【Linux系统编程】IO多路复用之epoll

    00. 目录 文章目录 00. 目录 01. 概述 02. epoll函数 03. 程序示例 04. epoll优缺点 05. 附录 01. 概述 epoll是Linux下多路复用IO接口select ...

  7. Socket IO多路复用: epoll原理图解

    目录 一.accept 创建新 socket 1.1 初始化 struct socket 对象 1.2 为新 socket 对象申请 file 1.3 接收连接 1.4 添加新文件到当前进程的打开文件 ...

  8. IO多路复用之epoll模型

    一.IO多路复用:一个线程监测多个IO操作 基本思想:先构造一张有关描述符的表,然后调用一个函数,当这些文件描述符中的一个或多个已经准备好进行I/O函数时才返回.函数返回时告诉进程哪个描述符已经就绪, ...

  9. IO多路复用机制——epoll

    高效地对海量用户提供服务,必须要让进程能同时处理很多个tcp连接.假设一个进程保持了10000条连接,如何发现哪条连接上有数据可读.可写? 实现:循环遍历来发现IO事件?效率太低了. 目录 IO模型 ...

最新文章

  1. 为你总结了N个真实线上故障,从容应对面试官!
  2. linux查看进程运行日志文件,【Linux】常用指令、ps查看进程、kill杀进程、启动停止tomcat命令、查看日志、查看端口、find查找文件...
  3. CentOS 7.8使用devtoolset-9使用高版本gcc version 9.3.1
  4. 自定义TBE算子入门,不妨从单算子开发开始
  5. H5页面移动端IOS键盘收起焦点错位
  6. PyCharm使用期间出现报错集合 持续更新ing
  7. java精确除法运算-BigDecimal
  8. 解方程c语言程序,C语言程序解线性方程组
  9. 【技术分享】select下拉框option默认选中(php模板渲染)
  10. 微软笔试题《Arithmetic Puzzles》- 题解
  11. ensp VRRP配置2
  12. Eclipse启动出错误:An error has occurred.See the log file。。。。
  13. Instead Of 触发器
  14. onselectstart和onselect的使用
  15. 【FusionCompute】基于FreeNAS部署并使用虚拟存储(五)
  16. android-第一行代码-第六章数据储存——持久化技术 含MMKV和Room新知识点(温故而知新)学习记录
  17. 【解决】瑞星杀毒软件无法卸载,rising进程无法关闭
  18. 介绍一下 ForkJoinPool
  19. java fastmethod_Java FastMath.cbrt方法代码示例
  20. Windows下vc开发chrome浏览器工程的一点粗浅理解和封装

热门文章

  1. mysql盲注脱裤_记一次猥琐的脱裤
  2. 上海育才高中2021高考成绩查询,育才中学2018高考成绩
  3. Hibernate实战——双向N-N关联
  4. C++11标准模板(STL)- 算法(std::nth_element)
  5. 推荐系统的混合加权技术研究
  6. 《一百岁感言》 杨绛
  7. 爬虫之BeautifulSoup
  8. 【asm基础】nasm和masm的一些区别
  9. 《Real-Time Rendering 4th Edition》全文翻译 - 第6章 纹理化(下)6.7 ~ 6.9
  10. e1000网卡驱动小结