libev源代码浅析
libev是一个开源的事件驱动库,基于epoll,kqueue等OS提供的基础设施。其以高效出名,它可以将IO事件,定时器,和信号统一起来,统一放在事件处理这一套框架下处理。
libev的基本使用方法如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
int main ( void ) { // use the default event loop unless you have special needs struct ev_loop *loop = EV_DEFAULT; // initialise an io watcher, then start it // this one will watch for stdin to become readable ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ); // 设置对stdin_watcher这个fd关注读事件,并指定回调函数 ev_io_start (loop, &stdin_watcher); // 激活stdin_watcher这个fd,将其设置到loop中 // initialise a timer watcher, then start it // simple non-repeating 5.5 second timeout ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.); //设置一个定时器,并指定一个回调函数,这个timer只执行一次,5.5s后执行 ev_timer_start (loop, &timeout_watcher); //激活这个定时器,将其设置到loop中 // now wait for events to arrive ev_run (loop, 0); //循环开始 // break was called, so exit return 0; } |
libev中有一个抽象概念,叫做watcher(ev_watcher),libev中有各种各样的watcher,比如定时器watcher(struct ev_timer),I/O watcher(struct ev_io) , 信号watcher(struct ev_signal)
等等。这三个具体的watcher相当于父类watcher的子类。这种继承关系在C++这种高级语言中已内置,在C中实现就需要一些技巧,libev的作者使用了宏。
"父类"ev_watcher定义如下:
typedef struct ev_watcher{ <strong><span style= "color: #ff0000;" >EV_WATCHER(ev_watcher)</span></strong>} ev_watcher; |
宏EV_WATCHER定义如下:
/* shared by all watchers */ #define EV_WATCHER(type) \ int active; /* private */ \ //该watcher是否被激活,加入到loop中 int pending; /* private */ \ //该watcher关注的events是否已触发 EV_DECL_PRIORITY /* private */ \ //int priority; 优先级,watcher是有优先级的 EV_COMMON /* rw */ \ // void *data; EV_CB_DECLARE (type) /* private */ // void (*cb)(struct ev_loop *loop, type *w, int revents);回调函数 |
再看一个"父类"ev_watcher_list的定义:
typedef struct ev_watcher_list{ EV_WATCHER_LIST (ev_watcher_list)} ev_watcher_list; |
宏EV_WATCHER_LIST定义如下:
#define EV_WATCHER_LIST(type) \ <strong><span style= "color: #ff0000;" > EV_WATCHER (type)</span></strong> \ struct ev_watcher_list *next; /* private */ |
可以看出,ev_watcher_list 其实也是ev_watcher的一个"子类", 它多了一个成员变量 struct ev_watcher_list *next;
这个成员变量用于将watcher串起来。
现在看一个I/O watcher 这个最重要的"子类":
typedef struct ev_io{ EV_WATCHER_LIST (ev_io) int fd; /* ro */ // 显而易见,与io相关联的fd int events; /* ro */ // 这个watcher在fd上关注的事件} ev_io; |
可以看出,ev_io是一种具体的watcher,它有两个自己专有的成员变量fd和events
下面看一下最关键的一个数据结构:
struct ev_loop{ ev_tstamp ev_rt_now; #define ev_rt_now ((loop)->ev_rt_now) // 这里decl是declare的意思,ev_vars.h 里面会定义一堆的变量,这些变量 // 都是本结构的成员,ev_vars.h展开的时候会用到下面这一行VAR的宏 #define VAR(name,decl) decl; #include "ev_vars.h" #undef VAR}; |
ev_vars.h中包含很多关键的成员,比如:
epoll相关的成员变量:
#if EV_USE_EPOLL || EV_GENWRAPVARx( struct epoll_event *, epoll_events) // 相当于struct epoll_event *epoll_events VARx( int , epoll_eventmax) //目前epoll_events数组的大小,可以扩充,每次以2倍的大小扩充VARx( int , backend_fd) // 对于epoll来说,就是epoll使用的fd//对于epoll来说,实际的函数是ev_epoll.c中的epoll_modify函数,这个函数会执行epoll_ctlVAR (backend_modify, void (*backend_modify)(EV_P_ int fd, int oev, int nev))<br> //对于epoll来说,实际的函数是ev_poll.c中的epoll_poll函数,这个函数会执行epoll_waitVAR (backend_poll , void (*backend_poll)(EV_P_ ev_tstamp timeout)) |
与fd相关的成员变量:
VARx(ANFD *, anfds) //这个数组是以fd为索引VARx( int , anfdmax) //上面数组的大小VARx( int *, fdchanges) // fdchangemax大小的数组,每个元素是一个fd,这个数组中存了所有epoll需要poll的fdVARx( int , fdchangemax) //数组的容量VARx( int , fdchangecnt) // 数组中实际的元素的大小 |
ANFD和fd一一对应,结构体ANFD如下:
typedef struct{ WL head; // typedef ev_watcher_list *WL; 关注同一个fd的事件的watcher的链表,一个fd可以有多个watcher监听它的事件 unsigned char events; /* the events watched for */ // watcher链表中所有watcher关注的事件的按位与 unsigned char reify; /* flag set when this ANFD needs reification (EV_ANFD_REIFY, EV__IOFDSET) */ //当这个结构体需要重新epoll_ctl则设置,说明关注的事件发生了变化 unsigned char emask; /* the epoll backend stores the actual kernel mask in here */ //实际发生的事件 unsigned char unused;#if EV_USE_EPOLL unsigned int egen; /* generation counter to counter epoll bugs */#endif#if EV_SELECT_IS_WINSOCKET || EV_USE_IOCP SOCKET handle;#endif#if EV_USE_IOCP OVERLAPPED or, ow;#endif} ANFD; |
维护所有的"所关注的事件发生了的watcher",这些watcher的callback最后都需要被调用
VAR (pendings, ANPENDING *pendings [NUMPRI]) //watcher是有优先级的,libev为每个优先级的watcher维护一个数组VAR (pendingmax, int pendingmax [NUMPRI]) // 每个优先级watcher数组的容量VAR (pendingcnt, int pendingcnt [NUMPRI]) // 每个优先级watcher数组实际大小 |
struct ANPENDING如下:
typedef struct{ W w; //typedef ev_watcher *W; int events; /* the pending event set for the given watcher */ //events只指这个watcher关注了的并且已经发生了的还没有处理的事件} ANPENDING; |
从示例程序可以看出,使用libev主要有几个方法:
ev_io_init
ev_io_start
ev_timer_init
ev_timer_start
ev_run
一个个看:
ev_io_init是个宏,主要就是设置ev_io中的各个成员
void ev_io_start(struct ev_loop *loop, ev_io *w) 会做如下几件事情:
1. 将参数w表示的watcher激活(w->active=1)
2. 将watcher w 加入到 w所关注的fd在anfds[](loop中)中相应的位置处的结构体ANFD中的watcher list链表中。
3. 将w所关注的fd加入到int *fdchanges数组中。
VARx(ANHE *, timers) |
struct ANHE定义如下:
/* a heap element */typedef struct { ev_tstamp at; //timer watcher 到期时间 WT w; // typedef ev_watcher_time *WT;} ANHE;typedef struct ev_watcher_time{ EV_WATCHER_TIME (ev_watcher_time)} ev_watcher_time;#define EV_WATCHER_TIME(type) \ EV_WATCHER (type) \ ev_tstamp at; /* private */ |
4. 调用backend_poll(loop, waittime)会做如下几件事:
对于使用epoll的系统来说,它实际上是调用ev_epoll.c 中的epoll_poll()函数,这个函数主要流程如下:
/* invoked when the given signal has been received */ /* revent EV_SIGNAL */typedef struct ev_signal{ EV_WATCHER_LIST (ev_signal) int signum; /* ro */ //信号id } ev_signal;ANSIG signals [EV_NSIG - 1]; // 每个信号的信息用一个ANSIG结构体表示typedef struct{ EV_ATOMIC_T pending;#if EV_MULTIPLICITY EV_P;#endif WL head; //可以有多个watcher关注同一个信号,使用链表串起来}ANSIG; |
VAR (evpipe, int evpipe [2]) // 用于将信号处理和事件处理框架结合在一起,evpipe[0]用于读,evpipe[1]用于写<br>VARx(ev_io, pipe_w) //这个I/O ev就是用于封装上面的pipe的读端,让epoll监听这个pipe_w,当接收到信号的时候,只需要在信号处理函数中往evpipe[1]中写即可 |
libev源代码浅析的更多相关文章
- Gradle 庖丁解牛(构建生命周期核心托付对象创建源代码浅析)
[工匠若水 http://blog.csdn.net/yanbober 未经同意严禁转载,请尊重作者劳动成果.私信联系我] 1 背景 上一篇<Gradle 庖丁解牛(构建源头源代码浅析)> ...
- 【Spark】Stage生成和Stage源代码浅析
引入 上一篇文章<DAGScheduler源代码浅析>中,介绍了handleJobSubmitted函数,它作为生成finalStage的重要函数存在.这一篇文章中,我将就DAGSched ...
- 【Spark Core】任务运行机制和Task源代码浅析1
引言 上一小节<TaskScheduler源代码与任务提交原理浅析2>介绍了Driver側将Stage进行划分.依据Executor闲置情况分发任务,终于通过DriverActor向exe ...
- Android网络通信Volley框架源代码浅析(三)
尊重原创 http://write.blog.csdn.net/postedit/26002961 通过前面浅析(一)和浅析(二)的分析.相信大家对于Volley有了初步的认识,可是假设想更深入的理解 ...
- Android网络通信Volley框架源代码浅析(二)
尊重原创 http://write.blog.csdn.net/postedit/25921795 在前面的一片文章Volley框架浅析(一)中我们知道在RequestQueue这个类中,有两个队列: ...
- java之Map源代码浅析
Map是键值对.也是经常使用的数据结构. Map接口定义了map的基本行为.包含最核心的get和put操作,此接口的定义的方法见下图: JDK中有不同的的map实现,分别适用于不同的应用场景.如线程安 ...
- Android网络通信Volley框架源代码浅析(一)
尊重原创http://blog.csdn.net/yuanzeyao/article/details/25837897 从今天開始,我打算为大家呈现关于Volley框架的源代码分析的文章,Volley ...
- bootstrap 3.0 LESS源代码浅析(一)
我一直以为Bootstrap的LESS源代码精髓在mixins.less,所以这个系列主要也是讲解mixins.less的. 什么是mixins? 混入,或者翻译成混合.官网的说法是:我们可以定义一些 ...
- Android应用Preference相关及源代码浅析(SharePreferences篇)
1 前言 在我们开发Android过程中数据的存储会有非常多种解决方式,譬如常见的文件存储.数据库存储.网络云存储等,可是Android系统为咱们提供了更加方便的一种数据存储方式.那就是SharePr ...
随机推荐
- c++ 简单的词法分析
scanner.h #include<iostream> #include<fstream> #include<string> using namespace st ...
- 在raw_input()中使用中文提示,在CMD下中文乱码问题解决。。。
错误的程序及结果: 解决1: guess = int(raw_input('请输入一个整数:'.decode('utf-8').encode('gbk'))) 解决2: guess = int(raw ...
- [转]IOS Segment页面之间view的切换
有三个view,分别为view1.view2.view3,通过UISegmentedControl进行三个view的切换. @interface UIViewDemoViewController : ...
- 帝国cms7.0判断字段为空,显示为不同
在一些情况下,我们的字段会留空,但是又不希望内容模板上只显示空.比如,在后台,如添加作者为小明,则显示小明,不填作者则显示"佚名",我们可以用以下代码. <? if($nav ...
- 二代USBKEY与一代USBKEY有什么区别?使用时需要注意什么?
二代USBKEY相较于一代USBKEY产品,增加了屏幕以及按键功能:可通过二代USBKEY产品的屏幕查看交易或操作信息,通过按键的方式进行上翻.下翻.确认.取消等操作. 二代USBKEY产品采用Mic ...
- 无法启动此程序,因为计算机中丢失QtCore4.dll。尝试重新安装该程序以解决此问题(在系统里添加3个路径)
解决方法: 计算机-属性-高级系统设置-高级-环境变量-系统变量-Path 添加 E:\Qt\4.8.5\bin; E:\Qt\4.8.5\qmake; E:\mingw\bin 重启计算机 http ...
- QBoxLayout::setSizeConstraint可以固定窗口的大小,且根据内部控件所占用的位置自动调节大小
setSizeConstraint(QLayout::SetFixedSize)的功能是保持整个对话框的尺寸保持相对固定.也就是说让布局管理器来调整对话框的大小.举个例子吧:一个上下可扩展对话框,基本 ...
- JAVA GUI学习 - JSplitPane分屏组件学习
public class JSplitPaneKnow extends JFrame { JSplitPane jSplitPane; JPanel jPanelRed; JPanel jPanelB ...
- Android 带password输入界面的Dialog实现机制
1.布局实现: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andr ...
- 百度——LBS.云 v2.0——云存储的POI创建和删除--Android 源码
如有疑问请联系:QQ936467727 需要注意的几点问题: 1.密钥是http://lbsyun.baidu.com/apiconsole/key申请的,密钥类型是浏览器端 2.geotable_i ...