epoll的工作原理和使用场景

对比epoll和select,就会发现select的缺点恰恰都是epoll的优点.下面进行epoll的学习:

epoll的相关系统调用的函数:

1.创建一个epoll句柄(即是一个文件)

int epoll_create(int size);
返回值:epoll_create的返回值是一个文件描述符.
参数:在现在的使用中,size没有什么意义,可以随便填写

2.epoll事件的添加

int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
参数:
epfd:就是epoll_create1的返回值.
op:表示文件描述符的动作:
①将新的文件描述符添加进epfd.
②将已经添加的文件描述符进行修改.
③从epfd中删除对应的文件描述符.
fd:表示现在要操作的文件描述符
struct epoll_event *event:是一个结构体的输出型参数.其实是key_value形式.
该结构体包含:
struct epoll_event
{
    uint32_t events;      //这是一个位图,就是存储对应的文件描述符的状态,该状态有很多形式
    epoll_data_t data;    //这是一个联合体
}__EPOLL_PACKED;
events的状态有:EPOLLIN(表示文件描述符可读),EPOLLOUT(表示文件描述符符可写),EPOLLET(epoll的边缘触发),EPOLLERR(表示文件描述符发生错误),EPOLLHUP(表示文件描述符将被挂起);
typedef union epoll_data
{
    void* ptr;   
    int fd;   //表示添加的文件描述符,在进行epoll_wait函数后可以进行返回.
    uint32_t u32;
    uint64_t u64;
}epoll_data;

3.等待就绪文件描述符

int epoll_wait(int epfd,struct epoll_event *event,int maxevents,int timeout);
参数:
epfd:表示epoll_create的返回值,即文件句柄.
struct epoll_event *event:是一个输出型参数,表示将就绪的文件描述符给赋值到events中.
maxevents:是第二个参数的个数.
timeout:超时时间.(0表示非阻塞,-1表示阻塞)
该函数的返回值:
表示就绪的文件描述符的个数.

epoll的工作原理

首先,epoll的使用过程:

  • 调用epoll_create()函数创建一个epoll句柄(结束之后要记得关闭epoll句柄).
  • 调用epoll_ctl()函数将监控的文件描述符进行处理.
  • 调用epoll_wait()函数,等待就绪的文件描述符.

epoll的工作原理:
总体的工作原理:当我们调用epoll_create系统函数时,会在内核中建一颗红黑树,红黑树的节点就是调用epoll_ctl()系统函数时管理的需要监控的事件。此外,还会创建一个链表(即就绪队列),就是用来存储就绪的事件。而当调用epoll_wait()系统函数时,就仅仅只需要查看该链表之中有没有数据,有就会将链表上的数据从内核态拷贝到用户态,同时将事件的数量返回给用户,这个事件复杂度是O(1)。若没有就会立即返回,然后继续的等待,若等待的时间超过了timeout也会立即返回。
问题:那么,内核是怎么维护这个链表(就绪队列)?
这又涉及到了硬件设备,当调用epoll_ctl()进行注册事件时,会做2件事:一是将需要注册的事件挂载到红黑树之中,二是也给每个事件注册一个回调函数,建立一种回调关系。当有事件进入就绪后,就会触发网卡驱动程序发出中断,将对应的事件从红黑树中找到并添加到链表之中。这都是在内核中并结合硬件来实现的,无疑是非常之高效。


epoll的优点

  • 文件描述符的个数无上限.(只要内存足够大)将新的文件描述符通过epoll_ctl添加到红黑树中,由红黑树这个数据结构来管理所有需要监视的文件描述符.
  • 通知文件描述符已经就绪的方式:每一个文件描述符都会与硬件设备(网卡)绑定,当文件描述符就绪时,就会触发网卡去将对应的就绪的文件描述符回调,然后将其添加到队列之中,这个队列是一个双向链表.
  • 维护就绪队列:当文件描述符就绪时,就会放入内核中的就绪队列之中.当调用函数epoll_wait()函数时,若该队列之中有元素就会被取走,这样的操作时间复杂度是O(1);

epoll的工作方式

epoll的两种工作方式:1.水平触发(LT)2.边缘触发(ET)
LT模式:若就绪的事件一次没有处理完要做的事件,就会一直去处理。即就会将没有处理完的事件继续放回到就绪队列之中(即那个内核中的链表),一直进行处理。
ET模式:就绪的事件只能处理一次,若没有处理完会在下次的其它事件就绪时再进行处理。而若以后再也没有就绪的事件,那么剩余的那部分数据也会随之而丢失。
由此可见:ET模式的效率比LT模式的效率要高很多。只是如果使用ET模式,就要保证每次进行数据处理时,要将其处理完,不能造成数据丢失,这样对编写代码的人要求就比较高。
注意:ET模式只支持非阻塞的读写:为了保证数据的完整性。
若需要看代码的实现请移步->基于LT模式实现的TCP的服务器 基于ET模式实现的TCP的服务器


分析select/epoll的应用场景

纵使epoll的优于select,但是并不是在任何情况下都适合用epoll。因为epoll需要创建红黑树,需要建立链表,需要有回调机制。这些都是一定的开销。
首先我们回忆一下select和epoll对应的工作原理。
select:
select的工作流程:创建socket->绑定端口bind->监听listen->accept->write/read。当有客户端连接到来时,select会把该连接的文件描述符放到fd_set(文件描述符(fd)的集合),然后select会循环遍历它所监测的fd_set内的所有文件描述符,如果没有一个资源可用(即没有一个文件描述符可以进行read/write可以操作),则select让该进程阻塞的等待,一直等到有资源可用为止。而fd_set是一个类似于数组的数据结构,由于它每次都要遍历整个数组,所以它的效率会随着文件描述符的数量增多而明显的变慢,除此之外在每次遍历这些描述符之前,系统还需要把这些描述符集合从内核copy到用户空间,然后再copy回去,如果此时没有一个描述符有事件发生(例如:read和write)这些copy操作和便利操作都是无用功,可见slect随着连接数量的增多,效率大大降低。可见如果在高并发的场景下select并不适用,况且select默认的最大描述符为1024。
epoll:
首先调用epoll_create时内核帮我们在epoll文件系统里建了个file结点,只用来服务于epoll;除此之外在内核里建立红黑树,epoll_ctl的注册事件会挂载在这颗红黑树之中。当有新的socket连接来时,先遍历红黑树中有没有这个socket,如果有就立即返回,没有就插入红黑树之中,然后给内核中断处理程序注册一个回调函数,每当有事件发生时就通过回调函数把这些文件描述符放到事先准备好的用来存储就绪事件的链表中,调用epoll_wait时,会把链表中的socket拷贝到用户态内存,然后清空准备就绪list链表,最后检查这些socket。在LT模式下,如果这些socket上确实有未处理的事件时,该句柄会再次被放回到刚刚清空的准备就绪链表,保证所有的事件都得到正确的处理。如果到timeout时间后链表中没有数据也立刻返回。因此在并发需求量高的场景中我们即使要监控数百万计的句柄,大多数一次也只返回很少量的准备就绪句柄。由此可见epoll仅需要从内核态copy少量的句柄到用户态,这样就避免了select模型中的无效遍历和用户和内核之间的copy操作。
所以对于高并发的情景时非常适合epoll的:那么下面这样的情况就并不适用于epoll了。
一个游戏服务器,tcp server负责接收客户端的连接,dbserver负责处理数据信息,一个webserver负责处理服务器的web请求,gameserver负责游戏的逻辑处理,所有这些服务都和另外一个gateserver相连,gateserver负责服务器间的通信和转发(进程间通信),只要游戏服务器在服务状态,这些连接几乎不会断开(异常情况可能会断开),并且这些连接数量一般不会很多。这种情况,select还是epoll呢?很明显是select,因为每时每刻这些连接的socket都有事件发生(比如:服务期间的心跳信息,还有大型网络游戏的同步信息(一般每秒在20-30次)),最重要的是,这种场景下,并发量也不会很大。如果此时用epoll,为此所建立的文件系统,红黑书和链表对于此来说就是杀鸡用牛刀,效率反而不高。当然这里的tcp server负责大量的客户端的连接,毫无疑问epoll是首选,它接受大量的客户端连接,收到客户端的消息之后把消息转发发给select网络模型的gateserver,gateserver再转发给gameserver进行逻辑处理,最后返回给客户端就over了。因此在如果在并发量低,socket都比较活跃的情况下,select就不见得比epoll慢了(就像我们常常说快排比插入排序快,但是在特定情况下这并不成立)。

  • 5
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值