libev是一個開源的事件驅動庫,基於epoll,kqueue等OS提供的基礎設施。其以高效出名,它能夠將IO事件,定時器,和信號統一塊兒來,統一放在事件處理這一套框架下處理。數組
libev的基本使用方法以下:數據結構
1框架
2less
3函數
4oop
5this
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 structev_watcher
{
"color: #ff0000;">EV_WATCHER(ev_watcher)
} 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的定義:
typedefstructev_watcher_list
{
EV_WATCHER_LIST (ev_watcher_list)
} ev_watcher_list;
宏EV_WATCHER_LIST定義以下:
#define EV_WATCHER_LIST(type) \
"color: #ff0000;"> EV_WATCHER (type) \
structev_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_intfd,intoev,intnev))
//對於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以下:
typedefstruct
{
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則設置,說明關注的事件發生了變化
unsignedcharemask; /* the epoll backend stores the actual kernel mask in here *///實際發生的事件
unsignedcharunused;
#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, intpendingmax [NUMPRI]) // 每一個優先級watcher數組的容量
VAR (pendingcnt, intpendingcnt [NUMPRI]) // 每一個優先級watcher數組實際大小
struct ANPENDING以下:
typedefstruct
{
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數組中。
void ev_run(struct ev_loop *loop, int flags)最重要的函數,作以下幾件事:
1. 調用fd_reify。
遍歷fdchanges數組:針對其中的每一個fd,作以下事情:
將關注這個fd的全部的watcher都從ANFD的鏈表中取出來(經過fd去anfds[]中找到相應的ANFD結構體),而後將全部這些watcher關注的events經過epoll_ctl 給設置到
kernel中。而後清空fdchanges數組,其實就是將fdchangecnt置爲0。
2. time_update 更新時間,校準時間
3. 計算給epoll_wait()使用的timeout,從維護全部的timer的最小堆中取堆頂(timers [HEAP0]),而後減去當前時間獲得timeout。最小堆使用一個數組實現的,每一個元素是
struct ANHE。
timers定義以下:
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()函數,這個函數主要流程以下:
4.1. 調用epoll_wait()
4.2. 遍歷每一個返回的 struct epoll_event,取出fd,和epoll_event中激活的事件,調用fd_event (loop, fd, got)函數。
這個函數會更新loop的pending數組,將那些已經關注了一些事件的watcher,而且確實發生了的這些watcher給設置到loop的pending數組中
注意這裏,got是全部watcher關注的事件的總和。ANPENDING結構體中的events僅僅單個watcher關注的events。
5. time_update 再次更新校準時間
6. timers_reify(loop) 若是最近的定時器已經觸發過了,則從新選出下一個最近的定時器,將其置於堆頂。
7.periodics_reify(loop) 和timers處理差很少
8. idle_reify(loop) 沒關注
9. EV_INVOKE_PENDING 從loop的pending數組中依次調用各個watcher的回調函數,優先級從高到低
10.若是當前loop中還有被激活的watcher,而且loop_done ==0 而且啓動ev_run(flags)的參數沒有設置EVRUN_ONCE和EVRUN_NOWAIT,則繼續從1開始。
到目前爲止,已經將timer和I/O事件進行統一了,如今看信號是如何統一到事件處理框架中的。
看看signal watcher的定義:
/* invoked when the given signal has been received */
/* revent EV_SIGNAL */
typedefstructev_signal
{
EV_WATCHER_LIST (ev_signal)
intsignum;/* ro *///信號id
} ev_signal;
ANSIG signals [EV_NSIG - 1]; // 每一個信號的信息用一個ANSIG結構體表示
typedefstruct
{
EV_ATOMIC_T pending;
#if EV_MULTIPLICITY
EV_P;
#endif
WL head;//能夠有多個watcher關注同一個信號,使用鏈表串起來
}ANSIG;
loop中和信號相關的主要成員以下:
VAR (evpipe, intevpipe [2])// 用於將信號處理和事件處理框架結合在一塊兒,evpipe[0]用於讀,evpipe[1]用於寫
VARx(ev_io, pipe_w) //這個I/O ev就是用於封裝上面的pipe的讀端,讓epoll監聽這個pipe_w,當接收到信號的時候,只須要在信號處理函數中往evpipe[1]中寫便可
和I/O事件,timer事件相似,有以下兩個過程:
ev_signal_init : 宏,初始化這個ev_signal結構體
void ev_signal_start(struct ev_loop *loop, ev_signal *w)作以下幾件事:
1. 激活ev_signal這個watcher(w->active = 1)
2. 將w串到signals數組中相應位置的watcher list中
3. 初始化一對pipe,這對pipe就是用於將信號和事件處理框架結合起來,而且註冊一個ev_io pipe_w到epoll中,pipe_w封裝的是int evpipe[2]的讀端evpipe[0]
4. 註冊這個信號的信號處理函數ev_sighandler,信號處理函數會將signals數組中相應的槽位的pending置1,意思是已接收到這個信號,而且往int evpipe[2]的寫端
evpipe[1]寫入一個字節,這樣在pipe_w這個IO event就有了讀事件,從而epoll_wait()返回,成功的將信號和事件處理框架結合起來。
至此,libev如何將IO 事件,timer事件和信號統一到事件處理框架下已分析完成。能夠看出,libev和libevent的作法是同樣的,只是實現不同,好比,關於繼承的實現,libev使用宏來實現至關的難讀,可是這樣作是高效的,借用霸爺的話說:"在libev的世界中,效率第一"。這可能正是大多數人說,libev比libevent代碼難讀的緣由