设置完需要监听的事件之后,就开始event loop了。在Libev中,该工作由ev_run函数完成。它的大致流程如下:

int
ev_run (EV_P_ int flags)
{
do
{
/* 执行EV_FORK类型事件 */ /* 执行EV_PREPARE类型事件 */ /* 遍历fdchanges数组,使用户关注的事件和epoll关注的事件保持一致 */
fd_reify (EV_A); /* 计算epoll阻塞时间 */ /* 事件驱动机制阻塞等待事件发生 */
backend_poll (EV_A_ waittime); /* 调整管理时间的堆 */
timers_reify (EV_A); /* relative timers called last */ /* 将EV_CHECK事件放入pendings数组中 */
if (expect_false (checkcnt))
queue_events (EV_A_ (W *)checks, checkcnt, EV_CHECK); /* 调用pendings数组中watcher的回调函数 */
EV_INVOKE_PENDING;
} return activecnt;
}

上一篇文章中说到fd监听的内容被修改后需要放入fdchanges数组中,将来的某个时刻再在事件驱动机制中更新,这个时刻就是调用fd_reify函数的时候。fd_reify函数代码如下:

inline_size void
fd_reify (EV_P)
{
int i; /* 1.遍历fdchanges数组中所有的fd,找到对应的ANFD
* 2.取出一个ANFD中所有watcher关注的事件
* 3.epoll_ctl更新
* 4.fdchanges数组清空
*/
for (i = ; i < fdchangecnt; ++i)
{
int fd = fdchanges [i];
ANFD *anfd = anfds + fd;
ev_io *w; unsigned char o_events = anfd->events;
unsigned char o_reify = anfd->reify; anfd->reify = ; /*if (expect_true (o_reify & EV_ANFD_REIFY)) probably a deoptimisation */
{
anfd->events = ; for (w = (ev_io *)anfd->head; w; w = (ev_io *)((WL)w)->next)
anfd->events |= (unsigned char)w->events; if (o_events != anfd->events)
o_reify = EV__IOFDSET; /* actually |= */
} if (o_reify & EV__IOFDSET)
backend_modify (EV_A_ fd, o_events, anfd->events); /* epoll_modify */
} fdchangecnt = ; /* fdchanges数组清空 */
}

前面说过,一个fd对应一个ANFD,一个ANFD可能包含多个watcher,上面函数就是遍历fdchanges数组,将数组中每个fd对应的所有watcher所关注的事件全部添加到事件驱动中,backend_modify宏内部最终会调用事件驱动机制对应的修改函数,例如epoll的epoll_modify函数。

回到ev_run函数,接下来调用backend_poll函数,这个函数最终会调用事件驱动机制,阻塞等待事件的发生。当监控的事件发生时,Libev提取这些事件,将它们放入一个pendings数组中,这个数组存储所有发生了但未处理的事件,放入过程如下:

/* 待运行的事件放入pendings数组中 */
void noinline
ev_feed_event (EV_P_ void *w, int revents) EV_THROW
{
W w_ = (W)w;
int pri = ABSPRI (w_); if (expect_false (w_->pending))
pendings [pri][w_->pending - ].events |= revents;
else
{
w_->pending = ++pendingcnt [pri];
array_needsize (ANPENDING, pendings [pri], pendingmax [pri], w_->pending, EMPTY2);
pendings [pri][w_->pending - ].w = w_;
pendings [pri][w_->pending - ].events = revents;
} pendingpri = NUMPRI - ;
}

pendings是个二维数组,第一维是watcher的优先级,第二维是同等优先级的watcher的不同下标。pendings中的元素类型为ANPENDING,定义如下:

typedef struct
{
W w; /* 关联的watcher */
int events; /* 发生的事件集合 */
} ANPENDING;

回到ev_run函数。这时,事件驱动机制检测到有事件发生后返回,并将事件对应的watcher保存在了pendings数组中。接下来就要执行事件对应的回调函数,也就是对“消费”该事件,EV_INVOKE_PENDING宏负责该动作。EV_INVOKE_PENDING宏最终会调用ev_invoke_pending函数,该函数定义如下:

/* 遍历pendings二维数组,执行事件触发回调函数 */
void noinline
ev_invoke_pending (EV_P)
{
pendingpri = NUMPRI; while (pendingpri) /* pendingpri possibly gets modified in the inner loop */
{
--pendingpri; while (pendingcnt [pendingpri])
{
ANPENDING *p = pendings [pendingpri] + --pendingcnt [pendingpri]; p->w->pending = ;
EV_CB_INVOKE (p->w, p->events);
EV_FREQUENT_CHECK;
}
}
}

外层while循环是按优先级递减,内层while循环同一优先级的ANPENDING按数组顺序进行遍历,遍历到的每一个ANPENDING都对应一个watcher,得到watcher之后就执行该watcher对应的回调函数。

至此,event loop完成一轮循环。

Libev学习笔记3的更多相关文章

  1. libev学习笔记

    转 libev的使用--结合Socket编程 作者:cxy450019566 之前自己学过一些libev编程的基础,这次写压测刚好用上了,才算真正动手写了些东西,在这里做一些总结.写这篇文章是为了用浅 ...

  2. Libev学习笔记4

    这一节首先分析Libev的定时器部分,然后分析signal部分. 对定时器的使用主要有两个函数: ev_timer_init (&timeout_watcher, timeout_cb, .) ...

  3. Libev学习笔记2

    这一节根据官方文档给出的简单示例,深入代码内部,了解其实现机制.示例代码如下: int main (void) { struct ev_loop *loop = EV_DEFAULT; ev_io_i ...

  4. Libev学习笔记1

    和Libevent相似,Libev是一个高性事件驱动框架,据说性能比Libevent要高,bug比Libevent要少.Libev只是一个事件驱动框架,不是网络库,因为它的内部并没有任何socket编 ...

  5. libev 学习使用

    libev 简单的I/O库.  a high performance full featured event loop written in c libev 的大小也比 libevent 小得多并且自 ...

  6. js学习笔记:webpack基础入门(一)

    之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...

  7. PHP-自定义模板-学习笔记

    1.  开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2.  整体架构图 ...

  8. PHP-会员登录与注册例子解析-学习笔记

    1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...

  9. 2014年暑假c#学习笔记目录

    2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...

随机推荐

  1. ##DAY11 UITableView编辑

    ##DAY11 UITableView编辑 每一个视图控制器都有一个编辑按钮,因为项目中编辑的应用场景非常多,所以系统预留了一个编辑按钮供我们使用 self.navigationItem.leftBa ...

  2. springmvc+mybatis集成配置

    简单之美,springmvc,mybatis就是一个很好的简单集成方案,能够满足一般的项目需求.闲暇时间把项目配置文件共享出来,供大家参看: 1.首先我们来看下依赖的pom: <!-- spri ...

  3. USACO Section 5.1 Musical Themes(枚举)

    直接枚举O(n^3)会TLE,只要稍微加点优化,在不可能得到更优解时及时退出.其实就是道水题,虽说我提交了6次才过= =..我还太弱了 -------------------------------- ...

  4. C++学习之使用new的注意事项

    C++学习之使用new的注意事项       在构造函数中使用new来初始化对象的指针成员成员时必须特别小心,具体的说,应该如下这样做: 一.如果在构造函数中使用new来初始化指针成员,则应该在析构函 ...

  5. CSS中的特殊性、继承、层叠

    前言 最近在看<CSS权威指南>,书中第三章“结构和层叠”对特殊性的解释十分到位,今天就来整理下思路,记录如下. 初来乍到,有何不妥请多多指点,有时间的话顺便评论下,讨论讨论~ 引入问题

  6. python 之日期时间处理

    ##python时间操作一般使用time.datetime两个模块 对于time模块,时间的表示模式有3种1.时间戳:time.time()2.字符串: time.strftime('%Y%m%d') ...

  7. 写了个小爬虫,为何用上代理ip总是出现错误。

    import urllib.request import re import os import random import threading def url_open(url): #在第8到第12 ...

  8. DX9三角形顶点绕序的判断

    今天亲自实践了下,终于搞清楚了. 多边形都有2个侧面,观察坐标系中正面朝向摄像机的为多边形为正面朝向多边形,正面偏离摄像机的为多边形为背面朝向多边形,d3d会把背面朝向多边形剔除,即背面消隐. 使用 ...

  9. LintCode-数组划分

    题目描述: 给出一个整数数组nums和一个整数k.划分数组(即移动数组nums中的元素),使得: 所有小于k的元素移到左边 所有大于等于k的元素移到右边 返回数组划分的位置,即数组中第一个位置i,满足 ...

  10. java中三种常见内存溢出错误的处理方法

    更多 10   相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的 ...