十二、I/O复用之epoll

一、epoll概述

前面我们学过了select,poll两种方式的I/O复用:

  • 它们都是采取轮询的方式查找是否有就绪描述符。
  • 都有数据结构从用户态拷贝到内核态,内核态拷贝到用户态这个过程。

假设现在有100万用户同时与服务器保持连接,但只有几十个连接是活跃通信,这就表示大量的连接中,只有一小部分连接会处于就绪状态。

select首先不能处理这种情况,因为它上限是1024个,当使用poll处理时,每次都需要轮询100万用户,判断是否就绪,每一次轮询都需要将100万的数组拷贝到内核。但是每次只有几十个连接会就绪,这时使用poll的弊端就出来了:

  • 100万连接中只有几十个会就绪,但是你需要遍历100万,浪费巨大的资源和事件
  • 每次都需要把100万大小的数组拷贝到内核态,导致效率低下

为了解决这种问题,在2.6内核时提出了epoll,是之前的select和poll的增强版本,是linux操作系统独有的I/O复用技术。相比于select和poll来说,epoll更加灵活。它没有了poll和select方法的弊端,使得I/O复用效率更高。

一、epoll函数

epoll是Linux系统上独有的,不像select,poll提供一个方法,它提供了一组方法,如下:

【1. 创建内核事件表:】

函数原型为:

#include <sys/epoll.h>
int epoll_create(int size)
          //成功返回内核事件表的标识符,失败返回-1
  • 功能:该函数 创建内核事件表用于存放描述符和关注的事件。调用这个函数的时候,在内核cache里建立了红黑树struct rb_root用于存储以后epoll_ctl传来的socket,也就是内核事件表;还建立了一个双向链表struct list_headrdllidt用于存储就绪事件。 当epoll_wait调用时,仅仅观察这个双向链表里有没有数据即可,有数据表示有就绪事件,直接返回。
  • size参数:表示创建的事件表需要多大,记住最后要close关闭,如果不关闭,那么就会导致fd被耗尽

【2. 管理内核事件表:】

可以对要监听的事件进行操作:注册,删除,修改;原型如下:

#include <sys/epoll.h>
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);
                 //成功返回0,失败返回-1.

参数:

  • epfd:内核事件比表的标识符。
  • op:标识操作,有:添加,删除,修改。不用自己写了,根据第二个op参数来选择
    EPOLL_CTL_ADD //注册新的文件描述符到内核事件表epfd中
    EPOLL_CTL_DEL //从内核事件表中删除文件描述符
    EPOLL_CTL_MOD //修改文件描述符
    
  • fd:要操作的文件描述符。
  • events:保存事件类型,用户填充告诉内核要对哪种事件进行操作。所以需要先定义event结构体,然后再进行操作,表示对何种事件类型进行何种操作
    struct epoll_event
    {
       short events;//事件类型:在每一个poll的事件类型标识前加个‘E’
       Union epoll_data_t data(联合体,用到其中的fd成员即文件描述符,其他的不用)
    }
    

假如现在我们将监听文件描述符添加到内核事件表中:

struct epoll_event event;
event.events=EPOLLIN;//监听读事件
event.data.fd=listenfd;//被监听的文件描述符
int res=epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&event);//将event结构体中存储的事件类型添加到内核事件表中。

【3. 开始监听内核事件表的事件】

int epoll_wait(int epfd,struct epoll_event events[],int maxevents,int timeout)
              // 成功返回就绪个数,失败-1,超时0

参数:

  • epfd:内核事件表;
  • events[]:events的数据是 由内核在epoll_wait返回时填充的,有事件就绪的文件描述符和就绪的事件类型;
  • maxevents:数组的长度,表示一次epoll_wait最多返回多少个就绪的文件描述符。
  • timeout:监听时间。

三、特点

【1. 速度快:】

  • select中为fd_set,poll 为 struct pollfd fds[],它们都是用户创建的,用户态存在,每调用一次select或者poll,都会存在两次用户态->内核,内核->用户的数据拷贝,一次是在调用时,一次是在返回时。这样数据结构越大,则越慢
  • epoll直接就在内核中记录,将其都记录在内核事件表上,在监听时不需要进行拷贝,所以速度比他俩快.

【 2. 查找时间复杂度低:】

  • select,poll返回就绪事件的个数,但不知道在哪,所以要轮询找,每次用户检索就绪事件的时间复杂度为O(N)。
  • epoll直接返回就绪事件链表,链表中有值就有就绪事件,所以每次都是就绪的不用找,每次用户检索就绪事件的时间复杂度为O(1)。

【3. 采取回调函数方式处理就绪事件】

  • select,poll采用轮询方式检测是否有事件发生,循环检测是否有事件就绪,直到找不到。适合关注的文件描述每次都有很大的机率就绪,1000个有900多个就绪,那么就很适合,但是1000个只有一个事件发生,还是要进行轮询1000多次,函数栈帧空间频繁,影响效果;
  • epoll的内核采用的是回调的方式检测文件描述符是否有事件发生。假设1000个,把文件描述符添加到事件表上,文件描述符在红黑树上挂载,除了值,事件类型,还带有一个回调函数,如果文件描述符有事件发生,它就调用回调函数,回调函数的作用是有事件就绪时就把文件描述符添加到双向链表中,返回时把链表的值拷贝到用户态1000个一个事件发生只会触发一次回调,不用多次循环。适合很多文件描述符,就绪事件描述符少。

四、epoll原理 && 事件类型

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值