IO多路转接之epoll

epoll是在2.5.44内核中被引进的一个新的API。


epoll的相关系统调用

epoll有三个相关的系统调用
epoll_create

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

用于创建一个epoll的句柄(本质是文件描述符)
·自从linux2.6.8之后,size参数是被忽略的
·用完之后,必须调用close()关闭
epoll_ctl

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

epoll的事件注册函数
·它不同于select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里(向红黑树)先注册要监听的事件类型
·第一个参数epfd是epoll_create()的返回值
·第二个参数表示动作,用三个宏来表示

EPOLL_CTL_ADD  //注册新的fd到新的epfd中
EPOLL_CTL_MOD //修改已经注册的fd的监听事件
EPOLL_CTL_DEL  //从epfd中删除一个fd

struct 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 */
  };

events是以下几个宏的集合
EPOLLIN:表示对应的文件描述符可读
EPOLLOUT:表示对应的文件描述符可写
EPOLLPRI:表示对应的文件描述符有紧急的数据可读
EPOLLERR:表示对应的文件描述符发生错误
EPOLLHUP:表示对应的文件描述符被挂起
EPOLLET:将EPOLL设置为边缘触发模式,
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列中
epoll_wait 时间复杂度O(1)

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

收集在epoll监控的事件中已经就绪的事件
·参数events是分配好的epoll_event结构体数组(也就是下面要说的就绪队列)
·epoll将就绪事件添加到events数组中,(events不可以是空指针,内核只负责把数据复制到这个events数组中,并不会帮助我们在用户态分内存)
·maxevents告诉内核这个events有多大,maxevents的值不能大于创建epoll_create()时的size
·timeout是超时时间(0,表示立即返回;1,表示永久阻塞)如果函数调用成功,返回对应IO上已经准备好的文件描述符数目,如果返回0,表示已经超时,返回小于0的值表示函数失败


epoll的工作原理

这里写图片描述
(1)首先在创建epoll模型时,操作系统会在内核中创建一个空的红黑树,红黑树中包含了系统中要关心哪个文件描述符当中的哪个内容。
(2)在内核中创建一个空的队列,当有就绪事件发生时,将该时间插入就绪队列当中
(3)建立回调映射机制,当网络底层有数据到来时,通过之前建立好的回调映射机制通知操作系统
epoll的优点
(1)文件描述符数目无上限,通过epoll_ctl()来注册一个文件描述符,内核中使用红黑树的数据结构来管理所有需要监控的文件描述符;而select、poll需要用户自己维护定义的数组
(2)基于事件的就绪通知方式;一旦被监听的某个文件描述符就绪,内核会采用回调机制,迅速激活这个文件描述符,这样随着文件描述符的增加,也不会影响判定就绪的性能;而select需要手动设置fd集合;poll使用一个pollfd的指针实现
(3)维护就绪队列:当文件描述符就绪,就会被放到内核中的一个就绪队列中,通过调用epoll_wait获取就绪文件描述符,只要取得队列中的元素即可,操作的事件复杂度为O(1)


epoll的工作方式

epoll有两种工作方式:水平触发(LT)和边缘触发(ET)
epoll默认状态下是水平触发
水平触发:当epoll检测到socket上有事件就绪时,就会一直通知,但可以不立刻进行处理,或者只处理一部分;直到缓冲区上所有数据都被处理完epoll_wait才不会立刻返回;支持阻塞读写和非阻塞读写
边缘触发:当epoll检测到socket上有事件就绪时,就立刻通知,并且只通知一次,必须立刻处理,循环式的把数据读完,文件描述符上的事件就绪后只有一次处理机会;ET比LT性能高,Nginx 默认采用epoll模式;epoll只支持非阻塞读写
为什么ET模式只支持非阻塞读写?
ET数据就绪时只会通知一次,如果用ET模式,当数据就绪时,需要一直调用read,直到出错或者完成为止,假若fd为阻塞方式,那么当读完缓冲区的数据时,如果对端没有关闭写端,那么该read函数会一直阻塞,影响其他文件描述符正常工作
epoll的使用场景:
对于多连接,且多连接中只有一部分连接比较活跃,比较合适使用epoll;
如果只是内部系统,服务器和服务器之间进行通信,只有少数的几个连接,这种情况下epoll并不合适,要根据具体场景选择。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值