libev是一個開源的事件驅動庫,基於epoll,kqueue等OS提供的基礎設施。其以高效出名,它可以將IO事件,定時器,和信號統一起來,統一放在事件處理這一套框架下處理。
libev的基本使用方法如下:
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
{
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的定義:
typedef struct ev_watcher_list { EV_WATCHER_LIST (ev_watcher_list) } ev_watcher_list;
宏EV_WATCHER_LIST定義如下:
#define EV_WATCHER_LIST(type) \
EV_WATCHER (type) \
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))
//對於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]用於寫
VARx(ev_io, pipe_w) //這個I/O ev就是用於封裝上面的pipe的讀端,讓epoll監聽這個pipe_w,當接收到信號的時候,只需要在信號處理函數中往evpipe[1]中寫即可
參考資料: