epoll源码结构分析

要理解epoll,最关键的就是它的结构设计,所以这里先从epoll的结构出发,请看下面一副简化的结构及联系图:



先介绍一下图中涉及到的各种结构体:

  • 先说明一下epitem结构体是什么,顾名思义,即为epollitem,epoll的基本单元,下面分别介绍一下几个主要的变量的含义:
  • struct list_head rdllink,或者取名为ready_list_link你会更容易理解,当epitem所对应的fd存在已经ready的I/O事件,则ep_poll_callback回调函数会将该结点链接到eventpoll中的rdllist循环链表中去,这样就将ready的epitem都串连起来了
  • struct epoll_filefd ffd,ffd中只包含一个fd及fd对应的file的指针
  • struct eventpoll *ep,eventpoll的指针,每个epitem都有这样一个指针,它指向对应的eventpoll变量,其实它的作用很简单,我们只要拿到了epitem,就可以根据它拿到eventpoll
  • struct event_poll event,还记得epoll_ctl的参数类型吗?其中就有一个event_poll指针,而该event即用来存放总用户空间拷贝的event_poll
  • 然后说明一下eventpoll结构体中主要的变量的含义:
    • struct list_head rdllist,ready_link_list,表示这是一个链表,事实上它就是一个循环链表,链表中的每个结点即为epitem中的rdllink,rdllist中链接的所有rdllink对应的epitem有事件ready
    • struct rb_root rbr,红黑树的根结点,其实每一个epitem中的第一个变量即为struct rb_node rbn;即表示红黑树的一个结点,所以rbr即是这样一颗红黑树,它的结点都为epitem变量,即相当于一个Set,将所有epitem管理起来,通过它可以很方便的增删改查epitem
  • 再看eppoll_entry结构体,它主要有这样几个变量:
    • void * base,base指向其对应的epitem
    • wait_queue_t wait,等待队列的项,wait中有一个唤醒回调函数指针,且该指针被初始化为ep_poll_callback,wait会被挂在到设备的等待队列中,等待设备的唤醒,当设备因状态改变,唤醒wait时,会执行ep_poll_callback,而ep_poll_callback会做这样一件事:list_add_tail(&epi->rdllink,&ep->rdllist),其中epi即为epitem变量,通过wait偏移拿到eppoll_entry,然后可以拿到base指针,即拿到了对应的epitem,而ep即为eventpoll变量,通过epitem的ep指针即可拿到,list_add_tail将epi的rdllink链到ep的rdllist中

下面结合这幅图大致讲解一下epoll_create、epoll_ctl、epoll_wait都在做些什么:

  • 首先,epoll_create会创建一个epoll的文件(epfd),同时创建并初始化一个struct eventpoll,其中file的private_data指针即指向了eventpoll变量,因此,知道epfd就可以拿到file,即拿到了eventpoll变量,这就是epoll_create所做的工作
  • epoll_ctl又做了什么事呢?首先大家看到了eventpoll中的rb_root红黑树吗?epoll_ctl其实就是在操作这颗红黑树,epoll_ctl有三种操作:
  • EPOLL_CTL_ADD:往红黑树中创建并添加一个epitem,对应处理函数为ep_insert
    在添加epitem时,也就是在ep_insert过程中,会创建一个eppoll_entry,并将其wait_queue挂载到设备的等待队列上,其中该wait_queue的唤醒回调函数为ep_poll_callback,当设备有事件ready而唤醒wait_queue时,就会执行ep_poll_callback将当前epitem链接到eventpoll中的rdllist中去,另外,如果在挂载wait_queue时就发现设备有事件ready了,同样会将epitem链接到rdllist中去。若每次调用poll函数,操作系统都要把current(当前进程)挂到fd对应的所有设备的等待队列上,可以想象,fd多到上千的时候,这样“挂”法很费事;而每次调用epoll_wait则没有这么罗嗦,epoll只在epoll_ctl时把current挂一遍(这第一遍是免不了的)并给每个fd一个命令“好了就调回调函数”,如果设备有事件了,通过回调函数,会把fd放入rdllist,而每次调用epoll_wait就只是收集rdllist里的fd就可以了——epoll巧妙的利用回调函数,实现了更高效的事件驱动模型

  • EPOLL_CTL_MOD:修改对应的epitem,对应处理函数为ep_modify
    在ep_modify过程中,处理会修改epitem对应的event值,同样会先查看一下对应设备的当前状态,如果有ready事件发生,则会将当前epitem链接到rdllist中去
  • EPOLL_CTL_DEL:从红黑树中删除对应的epitem,对应处理函数为ep_remove
    释放钩子、链接、资源空间等,如epitem所占的空间
  • 其实epoll_ctl已经将绝大部分事情都做了,epoll_wait有只需要收集结果就行了,它的目标也很单一,就看rdllist中是否有元素即可,当然,它还需要控制timeout,及结果转移,因为对于rdllist链接的epitem,只能说明其对应的fd有事件ready,但是哪些事件是不知道的,因此epoll_ctl再收集结果时,会亲自查看一下对应file的ready状态来写回events

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值