简介

一提到linux高性能服务器编程,epoll就是绕不开的话题,当前网络库在linux上实现也主要是以epoll为主。epoll的主要优点有:

  • 当检查大量的文件描述符时,epoll的性能比select和poll要高很多。
  • epoll 既支持水平触发也支持边沿触发。select 和 poll只支持水平触发,而信号驱动I/O只支持边缘触发。
  • 避免复杂的信号处理流程
  • 灵活性高,可以指定希望检查的事件类型

API

epoll由以下api构成:epoll_Create1, epollctl以及epoll_wait。

epoll_Create1

epoll_Create1 用于创建一个epoll实例,并返回代表该实例的文件描述符,epoll_Create1 支持传入一个可用来修改系统调用行为的flags参数,目前支持一个flag标志:EPOLL_CLOEXEC, 使得内核在新的文件描述符上启动执行即关闭标志。

epoll_ctl

epoll_ctl用来修改epoll实例中的兴趣列表,函数声明:

#include <sys/epoll.h>int epoll_ctl(int epfd, int op, int fd, struct epoll_event* ev);return 0 on success, or -1 on error复制代码

参数简介:

  • epfd - epoll实例
  • op 指定需要执行的操作
操作描述
EPOLL_CTL_ADD将描述符fd添加到opell实例中的兴趣列表中,如果文件描述符已经存在,则epoll_ctl返回EEXIST错误
EPOLL_CTL_MOD修改描述符fd上设定的事件,如果描述符不存在,epoll_ctl返回ENOENT错误
EPOLL_CTL_DEL将描述符fd从epoll实例兴趣列表中删除,如果描述符不存在,epoll_ctl返回ENOENT错误, 关闭文件描述符会自动的将其从所有的epoll实例兴趣列表中移除
  • fd - 文件描述符,可以是管道、FIFI、套接字、POSIX消息队列、终端、设备等,但不能是普通文件或目录的文件描述符(会出现EPERM错误)
  • ev

ev 是指向结构体epoll_event的指针,结构体定义如下:

struct epoll_event{uint32_t events;epoll_data_t data;};复制代码

events字段是一个位掩码, 表示描述符fd上感兴趣的事件集合。 data字段是一个联合体,联合体成员可以用来指定传回给调用进程的信息, 其类型为:

typedef union epoll_data{void      *ptr;int       fd;    uint32_t  u32;    uint64_t  u64;}epoll_data_t;复制代码

  • max_user_watches 上限

因为每个注册epoll实例中的描述符需要占用一小段不能被交换的内核内存空间,因此, 内核提供了一个接口用来定义每个用户可以注册到epoll实例上的文件描述符总数,这个上限值可以通过max_user_watches来查看和修改。 max_user_watches是linux系统/proc/sys/fs/epoll目录下的一个文件,上限值可以根据可用的系统内存来计算得出。

epoll_wait

epoll_wait返回epoll实例中处于就绪状态的文件描述符信息,单次epoll调用可以返回多个就绪态文件描述符信息。

#include <sys/epoll.h>int epoll_wait(int epfd, struct epoll_event *evlist, int maxevents, int timeout);复制代码

参数简介:

  • epfd - epoll实例
  • evlist - 指向包含就绪状态文件描述符的结构体数组,列表中每个元素代表就绪的文件描述符,events字段代表已经就绪的事件掩码,data字段是使用epoll_ctl注册事件时ev.data中所指定的值,也是同事件相关联的文件描述符。
  • maxevents - evlist的个数
  • timeout - 用来决定epoll_wait的行为:
timeoutepoll_wait的行为
-1epoll_wait将一直阻塞,直到兴趣列表中的文件描述符有事件产生
0epoll_wait 执行一次非阻塞式的检查
>0epoll_wait 至多阻塞timeout毫秒

调用成功之后,epoll_wait返回数组evlist中的元素个数,如果超时或者没有文件描述符就绪,则返回0, 出错时返回-1,并在errno中设定错误码以表示错误原因。 可以在一个线程中使用epoll_ctl将文件描述符添加到另一个线程中由epoll_wait所监视的epoll实例的兴趣列表中,并且,修改的兴趣列表将立刻得到处理。epoll_wait也会返回新加入文件描述符的就绪信息。

epoll 事件

epoll事件可以通过ev.events中的位掩码指定,events字段上的位掩码值

位掩码作为epoll_ctl的输入作为epoll_wait的返回描述
EPOLLIN可读取非高优先级的数据
EPOLLPRI可读取高优先级数据
EPOLLRDHUP套接字对端关闭
EPOLLOUT普通数据可写
EPOLLET 采用边缘触发事件通知
EPOLLONESHOT 完成事件通知之后禁用检查
EPOLLERR 有错误发生
EPOLLHUP 出现挂断
  • EPOLLONESHOT 一旦描述符设置了该标志,epoll_wait只返回一次事件通知,然后将文件描述符标记为非激活状态,如果希望继续收到事件通知,需要使用epoll_ctl的EPOLL_CTL_MOD操作重新激活对这个文件描述符的检查,而不能使用EPOLL_CTL_ADD操作。

LT和ET模式

epoll有水平触发(LT)和边沿触发(ET)两种模式,默认的是水平触发。

  • 水平触发:有事件就绪时,epoll_wait会一直返回该事件直到该事件被处理
  • 边沿触发:有事件就绪时,epoll_wait只会返回一次,降低同一事件被重复触发的次数,效率比LT高。

要使用边沿触发模式,需要在调用epoll_ctl时在ev.events字段中指定EPOLLET标志。

ET模式下的文件描述符饥饿现象

当epoll工作在ET模式并监视多个文件描述符时,其中一个文件描述符有大量的输入存在,在该文件描述符处于就绪态后,我们尝试通过非阻塞式的读操作将所有的输入都读取,那么此时就会有其它处于就绪的文件描述符得不到处理的风险,而处于饥饿状态。

饥饿现象的解决方案

应用程序维护一个列表,列表中存放着已经被通知位就绪态的文件描述符,通过一个循环按照如下方式不断处理:

  1. 调用epoll_wait监视文件描述符,并将处于就绪状态的描述符添加到应用程序维护的列表中,如果这个文件描述符已经注册到应用程序维护列表中,那么监视的时间应该设为较小的值或者0,这样应用程序就可以迅速进行到下一步,去处理已经处于就绪态的文件描述符。

2、应用程序只在那些已经注册为就绪态文件描述符上进行一定限度的I/O操作,而不是每次epoll_wait调用后都从列表头开始处理,当相关的非阻塞I/O系统调用出现EAGAIN或EWOULDBLOCK错误时,文件描述符就可以从应用程序维护的列表中移除了。


作者:用户3541072051383
链接:https://juejin.cn/post/6926767244747735048

©著作权归作者所有:来自51CTO博客作者wx607823dfcf6a9的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. 全面介绍 Linux 权限
  2. 【DB笔试面试265】在Oracle中,Oracle的SYS用户登录报权限不足的常
  3. SpringBoot如何加载jar包外面的配置文件?
  4. 别小看tail 命令,它难倒了技术总监
  5. linux文件操作
  6. 实现文件上下文管理(__enter__和__exit__)
  7. 一文读懂Linux
  8. 近2万字详解JAVA NIO2文件操作,过瘾!
  9. Python爬取酷狗音乐-详解(多图预警)

随机推荐

  1. 不能调用确定性可视性()从未看到PID的连
  2. android 百度地图app key 230 错误解决
  3. jni读取assets资源文件
  4. android studio 无法调试安装到小米手机
  5. 一篇不错的Android Audio架构代码梳理总
  6. [置顶] Animation之TranslateAnim
  7. OpenCV Android 打开前置后置摄像头
  8. android 百度地图路线规划去掉节点图标
  9. android插件apk杂记
  10. Android记录4--自定义ToggleButton+用Sha