Libev学习笔记4
这一节首先分析Libev的定时器部分,然后分析signal部分。
对定时器的使用主要有两个函数:
ev_timer_init (&timeout_watcher, timeout_cb, 5.5, .);
ev_timer_start (loop, &timeout_watcher);
和ev_io类型的watcher类似,timeout_watcher是一个类型为ev_timer的watcher,上面的ev_timer_init函数将它设置为5.5秒之后调用回调函数timeout_cb,最后一个参数0表示定时器不重复超时,执行完一次回调函数后就停止计时,如果最后一个参数为非0,那么回调函数第一次执行完之后,每个指定秒后回调函数会被重复执行。将ev_timer结构体展开:
typedef struct ev_timer
{
int active; /* private */ \ /* 非0表示watcher为激活状态,是periodics或timers数组的下标 */
int pending; /* private */ \ /* 非0表示watcher中有事件被触发,是pendings数组的下标 */
EV_DECL_PRIORITY /* private */ \ /* watcher的优先级 */
EV_COMMON /* rw */ \ /* void *data; */
EV_CB_DECLARE (type) /* private */ /* void (*cb)(EV_P_ struct type *w, int revents); */
ev_tstamp at; ev_tstamp repeat; /* rw */
} ev_timer;
其中at成员对应倒数第二个参数,表示多少秒后第一次触发超时;repeat成员对应最后一个参数,表示第一次超时触发之后每个多少秒重复触发。ev_timer_init函数实质上就是设置上面这些成员。ev_timer_start主要工作是将timer放入最小堆中,由最小堆统一管理所有的timer,代码如下:
/* 计算定时器的绝对触发事件并放入堆中 */
void noinline
ev_timer_start (EV_P_ ev_timer *w) EV_THROW
{
if (expect_false (ev_is_active (w)))
return; ev_at (w) += mn_now; /* 从现在开始经过at秒后触发 */++timercnt;
ev_start (EV_A_ (W)w, timercnt + HEAP0 - ); /* 将定时器w放入timers堆中 */
array_needsize (ANHE, timers, timermax, ev_active (w) + , EMPTY2);
ANHE_w (timers [ev_active (w)]) = (WT)w;
ANHE_at_cache (timers [ev_active (w)]); /* 更新heap */
upheap (timers, ev_active (w));
}
代码首先根据当前时间计算timer超时时刻的绝对时间,然后增加loop管理的timer个数timercnt,修改一些标志变量,最后把timer放入最小堆timers中,timers使用数组实现的一个最小堆,堆顶为离当前最近的一个timer。
timer定制完毕后就可以调用ev_run函数开始event loop了。在ev_run函数中,关于timer的代码流程大致如下:
int
ev_run (EV_P_ int flags)
{
.... do
{
....
/* 计算epoll等事件驱动机制的阻塞时间 */
{/* 堆顶取出ANHE,减去当前时间,赋值给waittime */
/* 调整waittime */ backend_poll (EV_A_ waittime); /* epoll_poll() */
}
....
/* queue pending timers and reschedule them */
/* 调整堆,取出所有超时的timer */
timers_reify (EV_A); /* relative timers called last */
/* 调用pendings数组中watcher的回调函数 */
EV_INVOKE_PENDING;
}
}
根据最小堆timers计算事件驱动机制的阻塞等待时间,阻塞返回后timers_reify函数记录所有超时的timer,最后由EV_INVOKE_PENDING执行这些timer对应的超时回调函数。在timers_reify函数中,如果timer是单次触发类型,那么会把该timer从最小堆中删除,如果timer是重复触发类型,那么会把该timer重新放入最小堆中,等待下次触发。
接下来分析信号部分。Libev将对信号的处理融入到了I/O事件的处理当中。官方文档给出了一个关于使用signal的例子:
static void
sigint_cb (struct ev_loop *loop, ev_signal *w, int revents)
{
ev_break (loop, EVBREAK_ALL);
} ev_signal signal_watcher;
ev_signal_init (&signal_watcher, sigint_cb, SIGINT);
ev_signal_start (loop, &signal_watcher);
ev_signal是专门监控是否有指定信号发生的watcher。在上面的例子中,当有SIGINT信号发生时,执行回调函数sigint_cb。ev_signal_init就是初始化ev_signal结构体,包括保存回调函数,保存需要等待的信号等。之后调用ev_signal_start函数,该函数是理解Libev中信号处理机制的关键,它的关键代码如下:
void noinline
ev_signal_start (EV_P_ ev_signal *w) EV_THROW
{
signals [w->signum - ].loop = EV_A; ev_start (EV_A_ (W)w, );
wlist_add (&signals [w->signum - ].head, (WL)w); if (!((WL)w)->next)
{
struct sigaction sa; evpipe_init (EV_A); sa.sa_handler = ev_sighandler; /* 设置信号处理程序 */
sigfillset (&sa.sa_mask);
sa.sa_flags = SA_RESTART; /* if restarting works we save one iteration */
sigaction (w->signum, &sa, );
}
}
其中,signals是一个数组,数组元素类型为ANSIG,它代表信号,数组中的一个元素代表Libev需要监控的一个信号。在Linux中,信号值是一个整数,那么用信号值作为下标,就能很快找到对应的ANSIG结构体了。ANSIG结构体定义如下:
/* 一个信号对应一个ANSIG */
typedef struct
{
EV_ATOMIC_T pending; /* 1表示收到信号 */
#if EV_MULTIPLICITY
EV_P; /* 信号所属的loop */
#endif
WL head; /* 多个watcher可以同时检测一个信号,用该成员作链表头 */
} ANSIG;
所以,ev_signal_start函数主要做了如下几件事:
- 在signals数组中建立一个新的需要监控的ANSIG,并设置ANSIG中的成员;
- 设置ev_signal这个watcher中的一些成员变量;
- 调用evpipe_init函数,该函数中会生成一个pipe,evpipe[0]用作读,evpipe[1]用作写,然后用ev_io监控evpipe[0];
- 使用sigaction系统调用设置Linux中标准的信号处理函数为ev_sighandler,信号处理函数中往evpipe[1]写入一个字节。
那么整理一下流程,当系统接收到指定信号后,可以得出如下一系列步骤:
- 捕捉信号
- 信号处理函数ev_sighandler被调用
- 在信号处理函数中向evpipe[1]写数据
- evpipe[0]变为可读
- 触发包裹它的pipe_w事件
- pipe_w事件对应的可读回调函数pipecb被调用
- pipecb函数首先会读取evpipe[0],然后根据信号值获得对应的ANSIG进而获得watcher,最后将该watcher放入loop->pendings数组中
- 该watcher对应的用户回调函数会在稍后被调用
以上就是Libev处理信号的大致流程。可以看出,对信号的监控最后都同一到了对文件描述符的监控,也就是最终使用到了ev_io类型的watcher。
参考:
http://my.oschina.net/u/917596/blog/177300
http://www.cnblogs.com/foxmailed/archive/2013/02/04/2891077.html
Libev学习笔记4的更多相关文章
- libev学习笔记
转 libev的使用--结合Socket编程 作者:cxy450019566 之前自己学过一些libev编程的基础,这次写压测刚好用上了,才算真正动手写了些东西,在这里做一些总结.写这篇文章是为了用浅 ...
- Libev学习笔记3
设置完需要监听的事件之后,就开始event loop了.在Libev中,该工作由ev_run函数完成.它的大致流程如下: int ev_run (EV_P_ int flags) { do { /* ...
- 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 ...
随机推荐
- C/C++中volatile关键字详解 (转)
1. 为什么用volatile? C/C++ 中的 volatile 关键字和 const 对应,用来修饰变量,通常用于建立语言级别的 memory barrier.这是 BS 在 "The ...
- Spring MVC返回的json如何去除根节点名称
spring xml中配置视图如果是如下 <property name="defaultViews"> <list> <bean class=&quo ...
- 64bits Python2.7.5安装numpy包
由于数值分析需要numpy计算包,我找了很多numpy-cp27的下载地址,下了最新版的.whl文件,但总是安装不成功,后来找到一个.exe文件 直接下载安装即可使用:下面是网址链接http://do ...
- Rem & Viewport
Rem布局 rem就是给根元素设置一个基准值 然后其他元素都以这个基准值作为单位 那么就可以在不同的手机上做出相同比例的元素了 事实上和百分比是同样的道理 网易和淘宝的rem 参考 http://ww ...
- Bluetooth 2.1+EDR是什么
目前应用最为广泛的是 Bluetooth 2.0+EDR标准,该标准在2004年已经推出,支持Bluetooth 2.0+EDR标准的产品也于2006年大量出现.虽然Bluetooth 2.0+EDR ...
- Java学习——Ubuntu下jdk的安装以及Java环境的配置
第一步.下载 到官网下载jdk,在Java SE Development Kit 7uXX里面,把Accept License Agreement 打勾,这样才能下载jdk,然后我们选择对应的Ubun ...
- cocos android分析
来自:http://xiebaochun.github.io/ cocos2d-x Android环境搭建 cocos2d-x环境搭建比較简单,可是小问题还是不少,我尽量都涵盖的全面一些. 下载软件 ...
- cocos2d-x-3.1 Text Labels(官方正式译文)
介绍 cocos2d支持(true type字体)标签,和纹理地图集标签. LabelTTF 标签的优缺点: 全部 TTF 字体的长处: 随意大小,支持调整字距. 易于使用. 不须要外部编辑器. 创建 ...
- xcode initWithCoder\awakeFromNib\layoutSubviews
控件通过xib,storyboard创建,初始化设置一定会调用initWithCoder awakeFromNib 加载完毕的时候肯定会调用 layoutSubviews 布局子控件 位置和尺寸 利用 ...
- AngularJS Directive 学习笔记
指令 Directive 指令要点 大漠老师的教学节点 解析最简单的指令 hello: 匹配模式 restrict 解析最简单的指令 hello: template.tempmlateUrl.$tem ...