Libev学习笔记3
设置完需要监听的事件之后,就开始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的更多相关文章
- libev学习笔记
转 libev的使用--结合Socket编程 作者:cxy450019566 之前自己学过一些libev编程的基础,这次写压测刚好用上了,才算真正动手写了些东西,在这里做一些总结.写这篇文章是为了用浅 ...
- Libev学习笔记4
这一节首先分析Libev的定时器部分,然后分析signal部分. 对定时器的使用主要有两个函数: ev_timer_init (&timeout_watcher, timeout_cb, .) ...
- Libev学习笔记2
这一节根据官方文档给出的简单示例,深入代码内部,了解其实现机制.示例代码如下: int main (void) { struct ev_loop *loop = EV_DEFAULT; ev_io_i ...
- Libev学习笔记1
和Libevent相似,Libev是一个高性事件驱动框架,据说性能比Libevent要高,bug比Libevent要少.Libev只是一个事件驱动框架,不是网络库,因为它的内部并没有任何socket编 ...
- libev 学习使用
libev 简单的I/O库. a high performance full featured event loop written in c libev 的大小也比 libevent 小得多并且自 ...
- js学习笔记:webpack基础入门(一)
之前听说过webpack,今天想正式的接触一下,先跟着webpack的官方用户指南走: 在这里有: 如何安装webpack 如何使用webpack 如何使用loader 如何使用webpack的开发者 ...
- PHP-自定义模板-学习笔记
1. 开始 这几天,看了李炎恢老师的<PHP第二季度视频>中的“章节7:创建TPL自定义模板”,做一个学习笔记,通过绘制架构图.UML类图和思维导图,来对加深理解. 2. 整体架构图 ...
- PHP-会员登录与注册例子解析-学习笔记
1.开始 最近开始学习李炎恢老师的<PHP第二季度视频>中的“章节5:使用OOP注册会员”,做一个学习笔记,通过绘制基本页面流程和UML类图,来对加深理解. 2.基本页面流程 3.通过UM ...
- 2014年暑假c#学习笔记目录
2014年暑假c#学习笔记 一.C#编程基础 1. c#编程基础之枚举 2. c#编程基础之函数可变参数 3. c#编程基础之字符串基础 4. c#编程基础之字符串函数 5.c#编程基础之ref.ou ...
随机推荐
- iOS手机号正则表达式并实现344格式 (正则的另一种实现方式)
[Demo下载地址]https://git.oschina.net/remainedmute/PhoneNumDemo.git 相关博客http://www.jianshu.com/p/00da4d8 ...
- IO库 8.1
题目:编写函数,接受一个istream&参数,返回值类型也是istream&.此函数须从给定流中读取数据,直到遇到文件结束标志时停止.它将读取的数据打印在标准输出上.完成这些操作后,在 ...
- Visual Studio 2013使用SASS和Compass--SASS和Compass安装
你需要安装ruby 你需要安装SASS/Compass 安装sass,在命令行中输入: $ gem install sass 你可能会问gem是什么?gem是ruby的包管理器.包的概念呢,就是一个为 ...
- Arduino Micro USB库
USBCore.cpp #define D_DEVICE(_class,_subClass,_proto,_packetSize0,_vid,_pid,_version,_im,_ip,_is,_co ...
- django学习之Model(五)MakingQuery
接着上篇. 10-一次更新多个对象 有时想要对QuerySet中的所有对象的某一个field来设定一个值,这时候可以像下边这样用update(): # Update all the headlines ...
- Html 小插件7 新闻免费代码
http://rss.cnfol.com/freecode/ 定制方法图:
- java 获取随机数的三种方法
方法1(数据类型)(最小值+Math.random()*(最大值-最小值+1))例:(int)(1+Math.random()*(10-1+1))从1到10的int型随数 方法2获得随机数for (i ...
- unity3d插件Daikon Forge GUI 中文教程-5-高级控件listbox和progress bar的使用
(游戏蛮牛首发)大家好我是孙广东.官网提供了专业的视频教程http://www.daikonforge.com/dfgui/tutorials/,只是是在youtube上,要观看是须要FQ的. 只是教 ...
- 关于javascript面向对象之闭包
要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量,而在函数外部无法 ...
- Lucence.Net+添加关键词+分页+排序
1.使用queryparser完成解析搜索请求 2.基本格式如: QueryParser parser=new QueryParser("字段名称","分析器实例&quo ...