本文参考社长的 TinyWebServer 庖丁解牛

epoll 常用API

epoll_create 函数

#include <sys/epoll.h>
int epoll_create(int size);

创建一个指示 epoll 内核事件表的文件描述符,该描述符将用作其他 epoll 系统调用的第一个参数,此处的 size 参数不起作用。

epoll_ctl 函数

#include <sys/epoll.h>
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

该函数用于操作内核事件表监控的文件描述符上的事件:注册、修改、删除:

  • epfd:为 epoll_create 的句柄;
  • op:表示动作,用 3 个宏来表示:
    • EPOLL_CTL_ADD:注册新的 fd 到 epfd;
    • EPOLL_CTL_MOD:修改已经注册的 fd 的监听事件;
    • EPOLL_CTL_DEL:从 epfd 删除一个 fd;
  • event:告诉内核需要监听的事件。

其中,event 是 epoll_event 结构体指针类型,表示内核监听的事件,具体定义如下:

struct epoll_event {
__uint32_t events;
epoll_data_t data;
};
  • events 描述事件类型,其中 epoll 事件类型有以下几种:

    • EPOLLIN:表示对应的文件描述符可读(包括对端SOCKET正常关闭)
    • EPOLLOUT:表示对应的文件描述符可写;
    • EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来)
    • EPOLLERR:表示对应的文件描述符发生错误;
    • EPOLLHUP:表示对应的文件描述符被挂断;
    • EPOLLLET:将 EPOLL 设置为边缘触发(ET)模式;
    • EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个 socket 的话,需要再次把这个 socket 加入到 EPOLL 队列中。

epoll_wait 函数

#include <sys/epoll.h>
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

该函数用于等待所监控的文件描述符上有事件的产生,返回就绪的文件描述符的个数。

  • events:用来存储内核得到的事件的集合;
  • maxevents:告知内核这个 events 有多大,这个值不能大于创建 epoll_create() 时的大小;
  • timeout:超时时间:
    • -1:阻塞;
    • 0:立即返回,非阻塞;
    • >0:指定毫秒数;
  • 返回值:成功返回有多少文件描述符就绪,时间到时返回 0,出错时返回-1。

select/poll/epoll 的区别

  • 调用函数

    • select 和 poll 都是一个函数,epoll 是一组函数;
  • 文件描述符数量

    • select 使用线性表保存文件描述符的集合,文件描述符有上限,一般是 1024,但可以修改源码,重新编译内核,不推荐;
    • poll 是使用链表存储文件描述符的集合,突破了文件描述符的上限;
    • epoll 使用红黑树存储文件描述符的集合,突破了文件描述符的上限(通过命令 ulimit -n number 修改,仅对当前终端有效);
  • 将文件描述符从用户传给内核:

    • select 和 poll 将所有文件描述符拷贝到内核态,每次调用都需要拷贝;
    • epoll 通过 epoll_create 建立一棵红黑树,通过 epoll_ctl 将要监听的文件描述符注册到红黑树上,文件描述符都在内核态;
  • 内核判断就绪的文件描述符:

    • select 和 poll 通过遍历文件描述符集合,判断哪个文件描述符上有事件发生;
    • epoll_create 时,内核除了会建立一个红黑树来存储以后 epoll_ctl 传来的 fd 外,还会再建立一个 list 链表,用于存储准备就绪的事件。当 epoll_wait 调用时,仅仅观察这个 list 链表上有没有数据即可;
    • epoll 是根据每个 fd 上面的回调函数(中断函数)判断,只有发生了时间的 socket 才会主动的去调用 callback 函数,其他空闲状态的 socket 则不会。若是就绪事件,则插入 list;
  • 应用程序索引就绪文件描述符:

    • select/epoll 只返回发生了事件的文件描述符的个数,若想要知道哪些文件描述符发生了事件,需要再次遍历;
    • epoll 返回的是发生了事件的个数和结构体数组,结构体包含 socket 的信息,因此直接处理返回的数组即可;
  • 工作模式:

    • select/poll 都只能工作在低效的 LT 模式下;
    • epoll 则可以工作在高效的 ET 模式,并且 epoll 还支持 EPOLLONESHOT 事件,可以进一步减少可读、可写和异常事件被触发的次数;

    其实 ET 和 LT 哪个高效也是针对不同的任务而言。

  • 应用场景:

    • 如果所有的 fd 都是活跃连接,epoll 需要建立红黑树和链表,效率反而不高,不如 select/epoll;
    • 如果监测的 fd 数目较小,且各个 fd 都比较活跃,建议使用 select/poll;
    • 如果监测的 fd 数目非常大,并且单位时间内只有其中一小部分 fd 处于就绪状态,这个时候使用 epoll 能够明显提升性能。

ET、LT、EPOLLONESHOT

  • LT 水平触发模式

    • epoll_wait 检测到文件描述符有事件发生,则将其通知给应用程序,应用程序可以不立即处理该事件;
    • 当下一次调用 epoll_wait 时,epoll_wait 还会再次向应用程序报告此事件,直至被处理。

    Note:

    一个事件只要有,就会一直触发。

    socket 上只要有未读完的数据,就会一直产生 EPOLLIN 事件。所以读完数据要移除事件,避免一直触发。

  • ET 边缘触发模式

    • epoll_wait 检测到文件描述符有事件发生,则将其通知给应用程序,应用程序必须立即处理该事件;
    • 必须要一次性将数据读取完,使用非阻塞 I/O,读取到出现 eagain。

    Note:

    只有一个事件从无到有,才会触发。

    socket 上每新来一次数据就会触发一次,如果某一次触发后,未将 socket 上的数据全部读完,也不会再次触发,除非再来一次数据。所以必须要一次性读完所有数据。如果未读完,需要再次将事件注册,

    ET 模式必须配合非阻塞 I/O 实现,因为 ET 模式会一次性读取完所有的数据,如果是阻塞 I/O 的话,会导致线程阻塞,影响重新调用 epoll_wait 来监听其他事件。

epoll 函数解析的更多相关文章

  1. UNIX网络编程学习指南--epoll函数

    epoll是select/poll的强化版,都是多路复用的函数,epoll有了很大的改进. epoll的功能 1.支持监听大数目的socket描述符 一个进程内,select能打开的fd是有限制的,有 ...

  2. ceph 初始化函数解析

    global_pre_init 预初始化函数,解析ceph.conf配置文件, 初始化定义global_context 和 config的全局变量. 全局预初始化函数 CINIT_FLAG_UNPRI ...

  3. [转]javascript eval函数解析json数据时为什加上圆括号eval("("+data+")")

    javascript eval函数解析json数据时为什么 加上圆括号?为什么要 eval这里要添加 “("("+data+")");//”呢?   原因在于: ...

  4. I/O多路复用——epoll函数

    1 select的低效率 select/poll函数效率比较低,主要有以下两个原因: (1)调用select函数后需要对所有文件描述符进行循环查找 (2)每次调用select函数时都需要向该函数传递监 ...

  5. PHP json_decode 函数解析 json 结果为 NULL 的解决方法

    在做网站 CMS 模块时,对于模块内容 content 字段,保存的是 json 格式的字符串,所以在后台进行模块内容的编辑操作 ( 取出保存的数据 ) 时,需要用到 json_decode() 函数 ...

  6. Matlab中bsxfun和unique函数解析

    一.问题来源 来自于一份LSH代码,记录下来. 二.函数解析 2.1 bsxfun bsxfun是一个matlab自版本R2007a来就提供的一个函数,作用是”applies an element-b ...

  7. epoll函数及三种I/O复用函数的对比

    epoll函数 #include <sys/epoll.h>int epoll_create(int size)int epoll_ctl(int epfd, int op, int fd ...

  8. socket使用TCP协议时,send、recv函数解析以及TCP连接关闭的问题

    Tcp协议本身是可靠的,并不等于应用程序用tcp发送数据就一定是可靠的.不管是否阻塞,send发送的大小,并不代表对端recv到多少的数据. 在阻塞模式下, send函数的过程是将应用程序请求发送的数 ...

  9. sigaction函数解析

    http://blog.chinaunix.net/uid-1877180-id-3011232.html sigaction函数解析  sigaction函数的功能是检查或修改与指定信号相关联的处理 ...

随机推荐

  1. jvm-learning-运行时数据区-整体

    在jdk8之后之前的方法区有叫做元数据. 每个JVM只有一个Runtime实例,即为运行时环境,相当于内存结构种的运行时数据区 线程 线程是一个程序里的运行单元,JVM允许一个应用有多个线程并行的执行 ...

  2. Zookeeper 的典型应用场景 ?

    Zookeeper 是一个典型的发布/订阅模式的分布式数据管理与协调框架,开发人员 可以使用它来进行分布式数据的发布和订阅. 通过对 Zookeeper 中丰富的数据节点进行交叉使用,配合 Watch ...

  3. 什么是多线程环境下的伪共享(false sharing)?

    伪共享是多线程系统(每个处理器有自己的局部缓存)中一个众所周知的性能问 题.伪共享发生在不同处理器的上的线程对变量的修改依赖于相同的缓存行,如 下图所示: 伪共享问题很难被发现,因为线程可能访问完全不 ...

  4. 运筹学之"概率"和"累计概率"和"谁随机数"

    概率 = 2/50 = 0.2 累计概率 = 上个概率加本次概率 案例1 概率=销量天数 / 天数 = 2 /100 = 0.02 累计概率 = 上个概率加本次概率 = 0.02 +0.03 = 0. ...

  5. glusterfs架构和原理

    分布式存储已经研究很多年,但直到近年来,伴随着谷歌.亚马逊和阿里等互联网公司云计算和大数据应用的兴起,它才大规模应用到工程实践中.如谷歌的分布式文件系统GFS.分布式表格系统google Bigtab ...

  6. 牛客网-剑指Offer 二维数组中的查找

    题目描述 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 ...

  7. [源码解析] TensorFlow 分布式环境(8) --- 通信机制

    [源码解析] TensorFlow 分布式环境(8) --- 通信机制 目录 [源码解析] TensorFlow 分布式环境(8) --- 通信机制 1. 机制 1.1 消息标识符 1.1.1 定义 ...

  8. Java实现单链表的合并(保证数据的有序性)

    一.思路 1.比较两个链表的大小 2.将小链表插入到大链表中 3.使用插入保证链表数据的有序性 二.核心代码 /** * 合并两个链表,并且按照有序合并 * @param singleLinkedLi ...

  9. 正则表达式小技巧,sql中in的字符串处理

    工作中我经常写sql,当写带in的语句时,需要敲好多单引号,逗号,敲写起来容易易出错.因此,我写了一个小工具,处理这种繁琐工作.原理简单,利用正则表达式匹配.替换. 先看界面,一个html页面,包含三 ...

  10. drf过滤和排序及异常处理的包装

    过滤和排序(4星) 查询所有才需要过滤(根据过滤条件),排序(按某个规律排序) 使用前提: 必须继承的顶层类是GenericAPIView 内置过滤类 内置过滤类使用,在视图类中配置,是模糊查询 使用 ...