另外两个重要的监控器
前面通过IO监控器将Libev的整个工作流程过了一遍。中间滤过了很多与其他事件监控器相关的部分,但是整体思路以及很明晰了,只要针对其他类型的watcher看下其初始化和注册过程以及在ev_run中的安排即可。这里我们再分析另两个常用的watcher
1.分析定时器监控器
定时器在程序中可以做固定周期tick操作,也可以做一次性的定时操作。Libev中与定时器类似的还有个周期事件watcher。其本质都是一样的,只是在时间的计算方法上略有不同,并有他自己的一个事件管理的堆。对于定时器事件,我们按照之前说的顺序从ev_init开始看起。
1.1定时器监控器的初始化
定时器初始化使用 ev_init(&timer_w,timer_action);
,这个过程和之前的IO类似,主要就是设置基类的active、pending、priority以及触发动作回调函数cb。
1.2设置定时器监控器的触发条件
通过 ev_timer_set(&timer_w,2,0);
可以设置定时器在2秒钟后被触发。如果第三个参数不是0而是一个大于0的正整数n时,那么在第一次触发(2秒后),每隔n秒会再次触发定时器事件。
其为一个宏定义 do { ((ev_watcher_time *)(ev))->at = (after_); (ev)->repeat = (repeat_); } while (0)
也就是设置派生类定时器watcher的“at”为触发事件,以及重复条件“repeat”。
1.3将定时器注册到事件驱动器上
ev_timer_start(main_loop,&timer_w);
会将定时器监控器注册到事件驱动器上。其首先 ev_at (w) += mn_now;
得到未来的时间,这样放到时间管理的堆“timers”中作为权重。然后通过之前说过的“ev_start”修改驱动器loop的状态。这里我们又看到了动态大小的数组了。Libev的堆的内存管理也是通过这样的关系的。具体这里堆的实现,感兴趣的可以仔细看下实现。这里的操作就是将这个时间权重放到堆中合适的位置。这里堆单元的结构为:
其实质就是一个时刻at上挂一个放定时器watcher的list。当超时时会依次执行这些定时器watcher上的触发回调函数。
1.4定时器监控器的触发
最后看下在一个事件驱动器循环中是如何处理定时器监控器的。这里我们依然抛开其他的部分,只找定时器相关的看。在“/ calculate blocking time/”块里面,我们看到计算blocking time的时候会先:
2
|
ev_tstamp to = ANHE_at (timers [HEAP0]) - mn_now;
|
3
|
if (waittime > to) waittime = to;
|
如果有定时器,那么就从定时器堆(一个最小堆)timers中取得堆顶上最小的一个时间。这样就保证了在这个时间前可以从backend_poll中出来。出来后执行timers_reify
处理将pengding的定时器。
在timers_reify
中依次取最小堆的堆顶,如果其上的ANHE.at小于当前时间,表示该定时器watcher超时了,那么将其压入一个数组中,由于在实际执行pendings二维数组上对应优先级上的watcher是从尾往头方向的,因此这里先用一个数组依时间先后次存下到一个中间数组loop->rfeeds中。然后将其逆序调用ev_invoke_pending
插入到pendings二维数组中。这样在执行pending事件的触发动作的时候就可以保证,时间靠前的定时器优先执行。函数feed_reverse
和 feed_reverse_done
就是将超时的定时器加入到loop->rfeeds暂存数组以及将暂存数组中的pending的watcher插入到pengdings数组的操作。把pending的watcher加入到pendings数组,后续的操作就和之前的一样了。回依次执行相应的回调函数。
这个过程中还判断定时器的 w->repeat 的值,如果不为0,那么会重置该定时器的时间,并将其压入堆中正确的位置,这样在指定的时间过后又会被执行。如果其为0,那么调用ev_timer_stop
关闭该定时器。 其首先通过clear_pending
置pendings数组中记录的该watcher上的回调函数为一个不执行任何动作的哑动作。
总结一下定时器就是在backend_poll之前通过定时器堆顶的超时时间,保证blocking的时间不超过最近的定时器时间,在backend_poll返回后,从定时器堆中取得超时的watcher放入到pendings二维数组中,从而在后续处理中可以执行其上注册的触发动作。然后从定时器管理堆上删除该定时器。最后调用和ev_start
呼应的ev_stop
修改驱动器loop的状态,即loop->activecnt减少一。并将该watcher的active置零。
对于周期性的事件监控器是同样的处理过程。只是将timers_reify
换成了periodics_reify
。其内部会对周期性事件监控器派生类的做类似定时器里面是否repeat的判断操作。判断是否重新调整时间,或者是否重复等逻辑,这些看下代码比较容易理解,这里不再赘述。·
2.分析信号监控器
分析完了定时器的部分,再看下另一个比较常用的信号事件的处理。Libev里面的信号事件和Tornado.IOLoop是一样的,通过一个pipe的IO事件来处理。直白的说就是注册一个双向的pipe文件对象,然后监控上面的读事件,待相应的信号到来时,就往这个pipe中写入一个值然他的读端的读事件触发,这样就可以执行相应注册的触发动作回调函数了。
我们还是从初始化-》设置触发条件-》注册到驱动器-》触发过程这样的顺序介绍。
2.1信号监控器的初始化
ev_init(&signal_w,signal_action);
这个函数和上面的一样不用说了
2.2设置信号监控器的触发条件
ev_signal_set(&signal_w,SIGINT);
该函数设置了Libev收到SIGINT信号是触发注册的触发动作回调函数。其操作和上面的一样,就是设置了信号监控器私有的(ev)->signum为标记。
2.3将信号监控器注册到驱动器上
这里首先介绍一个数据结构:
7
|
static ANSIG signals [EV_NSIG - 1 ];
|
EV_ATOMIC_T pending;
可以认为是一个原子对象,对他的读写是原子的。一个表示事件驱动器的loop,以及一个watcher的链表。
在ev_signal_start
中,通过signals数组存储信号监控单元。该数组和anfds数组类似,只是他以信号值为索引。这样可以立马找到信号所在的位置。从 Linux 2.6.27以后,Kernel提供了signalfd来为信号产生一个文件描述符从而可以用文件复用机制epoll、select等来管理信号。Libev就是用这样的方式来管理信号的。 这里的代码用宏控制了。其逻辑大体是这样的
这个是框架。其具体的实现可以参考使用signalfd和evpipe_init
实现。其实质就是通过一个类似于管道的文件描述符fd,设置对该fd的读事件监听,当收到信号时通过signal注册的回调函数往该fd里面写入,使其读事件触发,这样通过backend_poll返回后就可以处理ev_init
为该信号上注册的触发回调函数了。
在函数evpipe_init
里面也用了一个可以学习的技巧,和上面的#if XXX if() #endif {}
一样,处理了不支持eventfd
的情况。eventfd是Kernel 2.6.22以后才支持的系统调用,用来创建一个事件对象实现,进程(线程)间的等待/通知机制。他维护了一个可以读写的文件描述符,但是只能写入8byte的内容。但是对于我们的使用以及够了,因为这里主要是获得其可读的状态。对于不支持eventfd
的情况,则使用上面说过的,用系统的pipe
调用产生的两个文件描述符分别做读写对象,来完成。
2.4信号事件监控器的触发
在上面设置信号的pipe的IO事件是,根据使用的机制不同,其实现和触发有点不同。对于signalfd。
1
|
ev_io_init (&sigfd_w, sigfdcb, sigfd, EV_READ); /* for signalfd */
|
2
|
ev_set_priority (&sigfd_w, EV_MAXPRI);
|
3
|
ev_io_start (EV_A_ &sigfd_w);
|
也就是注册了sigfdcb函数。该函数:
1
|
ssize_t res = read (sigfd, si, sizeof (si));
|
2
|
for (sip = si; ( char *)sip < ( char *)si + res; ++sip)
|
3
|
ev_feed_signal_event (EV_A_ sip->ssi_signo);
|
首先将pipe内容读光,让后续的可以pengding在该fd上。然后对该signalfd上的所有信号弟阿勇ev_feed_signal_event
吧每个信号上的ANSIG->head上挂的watcher都用ev_feed_event
加入到pendings二维数组中。这个过程和IO的完全一样。
而对于eventfd和pipe则是:
1
|
ev_init (&pipe_w, pipecb);
|
2
|
ev_set_priority (&pipe_w, EV_MAXPRI);
|
3
|
ev_io_set (&pipe_w, evpipe [ 0 ] < 0 ? evpipe [ 1 ] : evpipe [ 0 ], EV_READ);
|
4
|
ev_io_start (EV_A_ &pipe_w);
|
pipe_w是驱动器自身的loop->pipe_w。并为其设置了回调函数pipecb:
05
|
read (evpipe [ 1 ], &counter, sizeof (uint64_t));
|
11
|
read (evpipe [ 0 ], &dummy, sizeof (dummy));
|
17
|
for (i = EV_NSIG - 1 ; i--; )
|
18
|
if (expect_false (signals [i].pending))
|
19
|
ev_feed_signal_event (EV_A_ i + 1 );
|
这里将上面的技巧#if XXX if() #endif {}
拓展为了#if XXX if() {} else #endif {}
。这里和上面的操作其实是一样的。后续操作和signalfd里面一样,就是读光pipe里面的内容,然后依次将watcher加入到pendings数组中。
libev源代码分析--事件监控器相关推荐
- UiAutomator喷射事件的源代码分析
上一篇文章<UiAutomator源代码分析之UiAutomatorBridge框架>中我们把UiAutomatorBridge以及它相关的类进行的描写叙述,往下我们会尝试依据两个实例将这 ...
- KVM虚拟机源代码分析
1,KVM结构及工作原理 1.1 KVM结构 KVM基本结构有两部分组成.一个是KVM Driver ,已经成为Linux 内核的一个模块.负责虚拟机的创建,虚拟内存的分配,虚拟CPU寄存器的读写以 ...
- CASSINI源代码分析
CASSINI源代码分析 2004-11-10 http://blog.csdn.net/shanhe/ 为什么要分析CASSINI? Cassini(卡西尼)是asp.net上的一个开源项目.主要给 ...
- Netty3 源代码分析 - NIO server绑定过程分析
Netty3 源代码分析 - NIO server绑定过程分析 一个框架封装的越好,越利于我们高速的coding.可是却掩盖了非常多的细节和原理.可是源代码可以揭示一切. 服务器端代码在指定 ...
- Android系统进程间通信(IPC)机制Binder中的Server启动过程源代码分析
原文地址: http://blog.csdn.net/luoshengyang/article/details/6629298 在前面一篇文章浅谈Android系统进程间通信(IPC)机制Binder ...
- Android应用Activity、Dialog、PopWindow、Toast窗体加入机制及源代码分析
[工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处.尊重劳动成果] 1 背景 之所以写这一篇博客的原因是由于之前有写过一篇<Android应用setCont ...
- tcpmp 编译 源代码分析
TCPMP源代码分析 TCPMP源代码分析 播放器主要由核心框架模块(common工程)和解码器.分离器插件组成.TCPMP的插件非常多,其中主要的插件有:interface插件实现了TCPMP的界面 ...
- 结合源代码分析一个完整的中断过程【转】
转自:http://blog.csdn.net/rosetta/article/details/49454021 转载请注明出处:http://blog.csdn.net/rosetta 结合源代码分 ...
- SDL2源代码分析1:初始化(SDL_Init())
===================================================== SDL源代码分析系列文章列表: SDL2源代码分析1:初始化(SDL_Init()) SDL ...
最新文章
- Caffe源码中math_functions文件分析
- 技术大咖齐聚爱数智慧人机交互技术论坛 | CNCC2021
- 盛会再临,2018中国大数据技术大会(BDTC)首曝日程及议题
- 网络负载均衡-DNS负载均衡
- Servlet配置错误处理页面/配置错误页面
- java判断读到末尾_Java Web入门之java--第一节 java 简介及开发环境安装
- Productivity Power Tools(Visual Studio 扩展) 最新亮点
- SNS:六度分隔理论
- 熊猫烧香病毒背后,网络高手对决一个月
- 负载均衡技术之负载均衡器简介
- 网页会屏蔽php代码,网站屏蔽360浏览器访问php源码js代码
- 听框架师——李云谈如何成为一个技术“牛人”
- CVPR2021目标跟踪汇总(一)
- Dependency ‘taglibs:standard:1.1.2‘ not found
- 计算机专业学生教师节礼物,已毕业学生送来最意外的教师节礼物 一份午餐让老师泪流满面...
- MySQL-触发器主子表
- Latch-up初认识
- Lotus Notes通过POP3和SMTP来收发internet邮件(设置domino邮件为例)
- 安卓模拟器右边的虚拟键盘消失了,怎样调出来
- pyhton:运算符重载(期权Greeks相加)
热门文章
- 宝塔 开启xdebug_XDebug的配置和使用
- Android开发之日期时间控件选择
- STM32CubeMX驱动ADS1118模块
- RT6862D/TR(6862D)
- 【Kafka从入门到成神系列 八】Kafka 多线程消费者及TCP连接
- python爬虫爬取百度贴吧图片,requests方法
- sed指令将逗号替换成换行符
- Threat Intelligence videos
- 2023管理员申请书怎么写最新范文
- SAS学习第11章:试验设计