【计算机网络】——I/O复用之epoll

1、epoll概述

1、引言
在我们之前对I/O复用技术中poll和select有一定的了解。

  • 那我们假设这样一个场景:有100万用户同时与一个进程保持着TCP连接,而每一时刻只有几十个或几百个TCP连接是活跃的(接收TCP包),也就是说在每一时刻进程只需要处理这100万连接中的一小部分连接。那么,如何才能高效的处理这种场景呢?进程是否在每次询问操作系统收集有事件发生的TCP连接时,把这100万个连接告诉操作系统,然后由操作系统找出其中有事件发生的几百个连接呢?

其实不然,进程收集有事件的连接时,其实这100万连接中的大部分都是没有事件发生的。因此如果每次收集事件时,都把100万连接的套接字传给操作系统,而由操作系统内核寻找这些连接上有没有处理的事件,将会是巨大的资源浪费。然而select和poll就是这样做的,因此他们最多只能处理几千个并发连接。

2、epoll概念
epoll是在2.6内核中提出的,是之前的select和poll的增强版本,是linux操作系统独有的I/O复用技术。相比于select和poll来说,epoll更加灵活。

2、epoll函数的功能和作用

epoll提供了一组方法。
1、创建一个内核事件表

int epoll_create(int size)
  • 功能:该函数生成一个 epoll 专用的文件描述符。调用这个函数的时候,在内核cache里建立了红黑树用于存储以后epoll_ctl传来的socket,还建立了一个双向链表用于存储准备就绪的事件,当epoll_wait调用时,仅仅观察这个双向链表里有没有数据即可,有数据就返回。
  • size参现在并不起作用,只是给内核一个提示,告诉它事件表需要多大。注意!!!在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽
  • 返回值:成功返回epoll专用的文件描述符,失败返回-1.

2、管理内核事件表

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
  • 功能:在这里注册要监听的事件类型
  • 参数:
    • epfd:epoll_create()的返回值
    • op:表示动作,用三个宏来表示
    EPOLL_CTL_ADD:注册新的 fd 到 epfd 中;
    EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
    EPOLL_CTL_DEL:从 epfd 中删除一个 fd。
    
  • fd:需要监听的文件描述符
  • events:保存事件类型,告诉内核要监听什么事件。调用它的时候才会产生一次拷贝,把用户的事件描述符拷贝到内核事件表中。
    struct epoll_event
     {
     	Short events;//事件类型:在每一个poll的事件类型标识前加个‘E’
     	Union epoll_data_t data(联合体,用到其中的fd成员即文件描述符,其他的不用)
     }
    
  • 返回值:成功返回0,失败返回-1

【举个栗子】假如现在我们将监听文件描述符添加到内核事件表中:

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)
  • 功能:监听内核事件表成功返回就绪个数
  • 参数
    • epfd:epoll专用的文件描述符,epoll_create()的返回值;
    • events:由内核在epoll_wait返回时填充的(有事件就绪的文件描述符和就绪的事件类型
    • maxevents:数组长度(表示一次epoll_wait最多返回多少个就绪的文件描述符)
    • timeout:监听时间,-1表示永久阻塞

总结
还是以刚刚我们假设的这个例子来加以说明。
1、调用epoll_create建立一个epoll对象
2、调用epoll_ctl向epoll对象中添加这100万个连接的套接字
3、调用epoll_wait收集发生事件的连接
这样,我们只需要在进程启动的时候建立一个epoll对象,并在需要的时候向它添加或删除连接就可以了。因此,在实际收集事件时,epoll_wait的效率就会非常高,因为调用epoll_wait时并没有向它传递100万个连接,内核也不需要去遍历全部连接

3、epoll的特点

  1. 速度快
    因为不论是select中使用的fd_set还是poll里面使用的struct pollfd fds[]。他们都是用户创建的,在用户态里面存在。每调用一次都会存在在用户态和内核态的数据拷贝,而且这种拷贝不止一次,一次是在调用时,一次是在返回时。而epoll直接就在内核中记录,在监听时不需要进行拷贝,速度比较快

  2. 用户检索就绪事件时间复杂度低
    在select和poll中都是以轮询的方式来检测就绪事件,时间复杂度是O(n)。但是在epoll中就直接返回就绪事件数组,所以每次都是就绪的,不用花费时间去寻找,时间复杂度就是O(1)

  3. 采用回调的方式检测检测文件描述符
    在select 和poll里面都是采用轮询的方式检测是否有事件发生。适合于关注的文件描述符每次都有很大的几率就绪的情况
    而epoll采用回调的方式。假设1000个,把文件描述符添加到事件表上,文件描述符在红黑树上挂载,除了值,事件类型,还带有一个回调函数。回调函数的作用就是有事件就绪时就把文件描述符添加到双向链表中,返回时把链表的值拷贝到用户态。1000个一个事件发生只会触发一次回调,不用多次循环。适合很多文件描述符,就绪事件描述符少

4、epoll原理 && 事件类型

1、原理

  • epoll 在初始化时向内核注册了一个特殊的文件系统,用来存储被监控的 socket,同时还会开辟出 epoll 自己的高速 cache 区,用于安置所监控的 fd,这些文件描述符以红黑树的形式存储在 cache 中,便于快速的插入、修改和删除。分配好想要的内存对象大小,每次使用时直接使用空闲的已分配好的对象就可以了。
  • epoll_create 函数首先会在虚拟的 epoll 文件系统中申请一个 file 结点,在内核 cache 中建立 RBTree,通过 epoll
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值