一  libev简介

  libev是一个轻量级的事件通知库,具备支持多种事件通知能力,通过对libev的源码的阅读,可以清楚了解事件通知实现内部机制。

二 核心数据结构

在libev中关键的数据结构是,loop结构体,该结构体定义的字段较多,但是主要核心的可以分为两大类

ev_loop结构体(loop为ev_loop结构的全局变量)的字段定义在ev_vars.h头文件中,然后在ev.c中通过include的方式导入

1.各类事件的watcher集合

 loop中有支持很多类型的事件,如下

ev_io                 // IO可读可写
ev_stat // 文件属性变化
ev_signal // 信号处理
ev_timer // 相对定时器
ev_periodic // 绝对定时器
ev_child // 子进程状态变化
ev_fork // fork事件
ev_cleanup // event loop退出触发事件
ev_idle // event loop空闲触发事件
ev_embed // 嵌入另一个后台循环
ev_prepare // event loop之前事件
ev_check // event loop之后事件
ev_async // 线程间异步事件

这些事件的监控管理都对应一种类型的watcher数组,比如

(loop)->anfds :维护所有fd事件

(loop)->timers :维护所有的定时器

(loop)->periodics:周期性事件

(loop)->prepares:该事件是loop启动之间就会执行的事件

等,每类事件都能在loop结构中找到对应的数组来维护对应的watchers。

2.2 watcher结构

    对于不同类型的事件的watcher,采用继承的方式来实现各个类型的watcher,libev是使用c的宏定义来实现继承(宏的奇技淫巧在libev中随处可见,这也导致libev看起来比较晦涩)

这个是公共watcher得到结构,是会被所有的子watcher所共享。

  active:表示在loop中对应的watcher数组中的下标

  pending & EV_DECL_PRIORITY:分别用来记录在全局loop->pendings中列 & 行下标(loop->pendings下文会介绍)

  EV_CB_DECLARE (type) :回调函数入口

比如io事件的watcher如上定义,类似的各种watcher都采取此类方式来生成(这样的好处是可以减少代码的重复度,降低了代码维护的成本,但是可读性方面也相应降低)

2.全局触发事件集合loop->pendings

loop->pendings记录管理当前已经发生等待调用回调函数的watcher集合,libev检查到事件发生后将对应的watcher加入到loop->pendings

 2.1 数据结构:

  *Watcher[N][M]:二位数组用来维护一轮循环下来,需要触发回调函数的watcher

  其中N:是每一类watcher的优先级

 另外还有loop->pendingcnt结构,也是一个二维数组,用来维护pending中每一行最大watcher下标。

以下是事件函数回调过程的代码,

本质上就是个遍历loop->pendings挨个调用watcher的注册的回调函数,可以看到按照优先级从高(小)到低(大),对于同一优先级watcher按照下标从大到小的方式来调用callBack函数。

值得注意的,就是pendingcnt结构,该结构维护当前每一行watcher数组当前的最大下标。

三 事件触发之io事件

  该节将会以IO事件在EPOLL平台的触发整个流程来阐述libev是如何来实现事件触发回调的整个过程。在介绍IO事件触发之前需要介绍一下相关的数据结构

       ANFD:用来记录对一个fd的所有监听事件,每一个监听事件使用链表结构进行组织。
  

  anfds:是ANFD*类型的数组,用来管理每一个fd的监听的事件

  changfds:是int*类型数组,每个元素记录当前发生更改的fd,比如加入新的监听fd,或者fd的监听事件发送修改。

在每次循环之前,都会对changefds和anfds的结构中对应的fd事件列表的所有event进行 | 操作,得到当前整个fd当前的监听事件和ANFD.events进行对比,如果没有修改将不会该表epoll的fd的监听事件,这样做的好处,避免了无效的修改,保证了所有对epoll的修改都是必须的,毕竟频繁对epoll进行修改代价还是挺大的。

好了,下面正式分析IO事件是如何触发的,当一个fd监听事件加入时

 step1:调用ev_io_start将watcher加入到anfds中,并将修改记录在changfds中

steps2: 调用fd_reify,通过比对changfds & anfds确定是否需要加入epoll事件,此时显然是需要的

    

    核心代码如下,第一处:暂时保存fd的events,第二处:通过遍历watcher链表计算新的events,比较前后是否发送变化,第三处:如果发生变化将修改epoll的监听事件

    自此完成监听事件的添加。

  step3:调用backend_poll函数得到当前监听已经发生的事件,

    #得到事件的fd & 已经发生的events,从anfds中获取对应的ANFD

  #遍历ANFD中的watcher链表,比对监听事件和已经发生的监听事件,如果符合将该watcher加入到loop->pendings,修改watcher中的pending变量标记在loop->pendings中的数组下标

  step4:遍历循环loop->pendings结构,挨个调用回调函数,从而完成事件触发的一个完整过程

四 事件触发之定时器事件

定时器采用小根堆的方式来维护所有的timer,libev整个过程是采用loop循环的方式,周期性的检测是否有事件发生

在loop中会获取当前堆顶的timer(最近要发生的)以及其他信息来计算当前可以sleep多长时间,从而可以保证进程在休眠的这段时间也不会有事件发生而没有及时通知。

五 ev_run函数解析

 ev_run将整个libev的各类事件通知流程串起。整个过程就是个大循环。

过程大致分为

1.检测是否有fork事件,如果有进行fork事件的回调函数

2.在loop之前调用prepare事件的回调函数。

3.检测fd的监听事件是否发生变化,是否需要修改epoll的监听事件

4.计算需要休眠的事件

     #根据定时器 & 周期任务 & timeout_blocktime(超时事件收集间隔事件) & io_blocktime(io事件收集间隔事件) 等信息计算此次循环需要sleep的时间

5.如果需要休眠则进行休眠

6.进程从休眠态唤起后,从epoll(pool,kqueue)中获取发生的事件,将对应的watcher加入到 loop->pendings中

7.将定时时间到了的定时器,加入loop->pendings中

8.收集周期任务,加入loop->pendings中

9.收集空闲事件加入loop->pendings中

   10.依此对loop->pendings中的watcher中注册的回调函数

自此整个loop完成,总体来说就是在整个loop中检测所有的监听的事件是否发生,然后依次对发生的事件,调用注册的回调函数。

六 源码文件结构

  #个平台网络编程接口,不同平台使用不同的文件,从而支持多平台

   ev_pool.c

  ev_port.c

  ev_kqueue.c

  ev_select.c

ev_vars.h:定义ev_loop数据结构,使用宏定义的方式进行定义

ev_warp.c:,使用宏定义的方式封装全局变量loop中字段的访问

ev.c:整个libev的核心部分,实现了整个libev的事件通知的大部分业务逻辑

七 总结

  libev从整个设计来看还是比较精巧的,大体上将整个事件通知机制划分为两个阶段

  #事件发生检测:

    各个事件检测过程实现不太一样(IO事件,定时器,周期性任务等各不一样),将检测到发生的事件加入loop->pendings中

  #事件回调触发

    对loop->pendings的事件,遍历依次触发回调函数

  整个libev可能作者出于对代码复用减少重复代码的原因,大量使用宏定义,甚至用宏定义实现了简单的继承关系,这也使得整个项目代码看起来比较晦涩难懂。

libev 源码解析的更多相关文章

  1. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  2. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  3. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  4. 多线程爬坑之路-Thread和Runable源码解析之基本方法的运用实例

    前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 前面 ...

  5. jQuery2.x源码解析(缓存篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 缓存是jQuery中的又一核心设计,jQuery ...

  6. Spring IoC源码解析——Bean的创建和初始化

    Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器 ...

  7. jQuery2.x源码解析(构建篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 笔者阅读了园友艾伦 Aaron的系列博客< ...

  8. jQuery2.x源码解析(设计篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 这一篇笔者主要以设计的角度探索jQuery的源代 ...

  9. jQuery2.x源码解析(回调篇)

    jQuery2.x源码解析(构建篇) jQuery2.x源码解析(设计篇) jQuery2.x源码解析(回调篇) jQuery2.x源码解析(缓存篇) 通过艾伦的博客,我们能看出,jQuery的pro ...

随机推荐

  1. Spring事务管理----------整合学习版

    作者:学无先后 达者为先 Spring提供了一流的事务管理.在Spring中可以支持声明式事务和编程式事务. 一  spring简介 1 Spring的事务       事务管理在应用程序中起着至关重 ...

  2. 2018多校第十场 HDU 6430 (线段树合并)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6430 题意:一棵树上每个节点权值为v[i],每个节点的heard值是:以它为LCA的两个节点的GCD的 ...

  3. [转]vue解决刷新页面vuex数据、params参数消失的问题

    一般项目都会有一些逻辑需要传递值给另一个页面,那么有的时候就会出现一个问题:用户刷新了页面,诶?数据没了,参数错误.那么今天经过总结,解决了这个问题.我在最新的项目中,通过了一下几种情况进行传值: 1 ...

  4. springbooot+restful目录规则

    dao是访问数据层,dto是数据传出层,po实体类

  5. 蓝桥历年试题 DNA对比

    [编程题](满分27分) 脱氧核糖核酸即常说的DNA,是一类带有遗传信息的生物大分子.它由4种主要的脱氧核苷酸(dAMP.dGMP.dCMT和dTMP)通过磷酸二酯键连接而成.这4种核苷酸可以分别记为 ...

  6. Mac下Tomcat安装&配置&80默认端口设置

    序言: 在学习Tomcat时, 部署虚拟服务主机时,遇到了无响应的情况.原以为是应为Tomcat默认端口8080在调整至(进行端口转发设置)默认端口80会和Mac自带Apache起冲突.但是也有同学使 ...

  7. [Linux]ubuntu更改国内源

    转自: https://blog.csdn.net/qq_35451572/article/details/79516563 推荐快速更新国内源 https://blog.csdn.net/qq_35 ...

  8. 西湖论剑2019--一道MISC题目的解题思路

    TTL题的writeup 第一次打西湖论剑,啥都不懂,被题目虐的很惨,一共就做出来两道题,但也算有收获.这里分享一下TTL那道misc题目的writeup,算是给自己点安慰吧. 题目描述 我们截获了一 ...

  9. 关于Math.random()

    关于 Math.random() ,以前经常搞混淆,这次写个笔记专门记录下: Math.random()  : 返回的是 0~1 之间的一个随机小数0<=r<1,即[0,1); 注意:这里 ...

  10. Hbuilder用自有证书打包 ios App上架AppStore流程

    最近在用Hbuilder做跨平台开发,经过一番研究终于在苹果商店上架成功了一款产品!这款产品就很简单,直接用hbuilder打包好,然后上传到商店即可.这里参照ios app提交应用商店 这篇文章结合 ...