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_GENWRAP VARx( 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_ctl VAR
(backend_modify, void (*backend_modify)(EV_P_
int fd,
int oev,
int nev))<br> //对于epoll来说,实际的函数是ev_poll.c中的epoll_poll函数,这个函数会执行epoll_wait VAR
(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的fd VARx( 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]中写即可 |