设置完需要监听的事件之后,就开始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. ASP.NET MVC View向Controller传值方式总结

    1:QueryString传值1)也可以使用new{}来为form的action增加querystring2)在controler里使用Request.QueryString["word&q ...

  2. Qt(QML)本地化

    Internationalization and Localization with Qt Quick 程序国际化 1) Use qsTr() for all  Literial UI strings ...

  3. C++中的栈和队列

    使用标准库的栈和队列时,先包含相关的头文件 #include<stack> #include<queue> 定义栈如下: stack<int> stk; 定义队列如 ...

  4. Ztree异步树加载

    JSP代码片段 <%@ page language="java" contentType="text/html; charset=utf-8" pageE ...

  5. 第25周五迷茫定位&转行理论建议

    今天下午请假办了无房证明和单身证明,准备开始贷款买房的征程,在犹豫纠结中我选择推进这个事情,之前的经验告诉我生活中可以面临改变或不改变境况的选择是要尽可能的选择改变,因为我还年轻.回来后知乎上看了一个 ...

  6. openstack windows 2008 img

    1,制作镜像主机pre Env yum -y install qemu-img virt-install libvirt 2,配置bridge

  7. 破解win7开机密码,又不取消密码

    Win7开机密码破解方法 1. 进入pe.(可用系统盘,或者u盘启动) 2.进入c:\windows\system32下 更改magnify.exe和cmd.exe的所有者为administrator ...

  8. UVA-514 Rails (栈)

      Rails  There is a famous railway station in PopPush City. Country there is incredibly hilly. The s ...

  9. python 类属性、对象属性

    类的普通属性: dir(Myclass), 返回一个key列表: Myclass.__dir__,返回一个字典: 1.类的数据属性: 2.类的方法: 类的特殊属性: 1.Myclass.__name_ ...

  10. ie6兼容性,还需要测试么?迷茫。。。

    最近公司网站在谷歌,火狐上测试都没有问题,但是在ietest,ie6上出现兼容问题 ,由于ietest好几次打开ie6都报错(尝试卸载重新安装几次无果),下载virtualbox安装自带ie6的xp系 ...