主頁:http://www.monkey.org/~provos/libevent/。html
libevent是一個事件觸發的網絡庫,適用於windows、Linux、bsd等多種平臺,內部使用select、epoll、kqueue等系統調用管理事件機制。linux
編譯庫代碼,編譯腳本會判斷OS支持哪一種類型的事件機制(select、epoll或kqueue),而後條件編譯相應代碼,供上層使用的接口仍然是保持統一的。算法
libevent支持用戶使用三種類型的事件,分別是網絡IO、定時器、信號三種。定時器的數據結構使用最小堆(Min Heap),以提升效率。網絡IO和信號的數據結構採用了雙向鏈表(TAILQ)。在實現上主要有3種鏈表:EVLIST_INSERTED, EVLIST_ACTIVE, EVLIST_TIMEOUT,一個ev在這3種鏈表之間被插入或刪除,處於EVLIST_ACTIVE鏈表中的ev最後將會被調度執行。windows
有許多開源項目使用libevent,例如memcached。使用libevent,使得memcached能夠適應多種操做系統。Libevent對底層異步函數提供了較薄封裝,庫自己沒有消耗過多性能;另外,使用堆排序管理定時器隊列,提供了較高的性能。網絡
//事件回調處理函數數據結構
Static void MyCallBack(const int fd, constshort which, void *arg)多線程
{異步
If(EV_READ==which){socket
//讀事件處理memcached
}
……
}
Int main(int argc, char** argv)
{
//初始化libevent
structevent_base *pEventBase;
pEventBase =event_init();
intsock=socket(……);
struct eventevent;
event_set(&event , sock, EV_READ | EV_PERSIST,MyCallBack, (void*)0 );
event_base_set(pEventBase, &event);
event_add(&event, 0);
event_base_loop(pEventBase, 0);
Return0;
}
event_init:初始化libevent庫。
event_set:賦值structevent結構。能夠用event_add把該事件結構增長到事件循環,用event_del從事件循環中刪除。支持的事件類型能夠是下面組合:EV_READ(可讀), EV_WRITE(可寫),EV_PERSIST(除非調用event_del,不然事件一直在事件循環中)。
event_base_set:修改structevent事件結構所屬的event_base爲指定的event_base。Libevnet內置一個全局的event_base結構。多個線程應用中,若是多個線程都須要一個libevent事件循環,須要調用event_base_set修改事件結構基於的event_base。
event_add:增長事件到事件監控中。
event_base_loop:事件循環。調用底層的select、poll或epoll等,如監聽事件發生,調用事件結構中指定的回調函數。
struct event g_clockevent;
struct event_base *g_pEventBase;
void clock_handler(const int fd, constshort which, void *arg)
{
staticbool initialized = false;
if(initialized) {
evtimer_del(&g_clockevent);
}
else {
initialized= true;
}
evtimer_set(&g_clockevent, clock_handler, (void*) 0);
//定時器時間
structtimeval t ;
t.tv_sec=1;
t.tv_usec=0;
event_base_set(g_pEventBase, &me->m_clockevent);
if(evtimer_add(&clock_handler, &t) == -1){
return;
}
//自定義事件處理
.....
}
int main(int argv, char** argc)
{
g_pEventBase=event_init();
clock_handler(0,0,(void*)0);
return0;
}
evtimer_set: 設置定時器事件。
evtimer_add: 增長定時器時間。
Libevent在底層select、pool、kqueue和epoll等機制基礎上,封裝出一致的事件接口。能夠註冊可讀、可寫、超時等事件,指定回調函數;當事件發生後,libevent調用回調函數,能夠在回調函數裏實現自定義功能。前面例子已經展示瞭如何使用libevent接口。
本節探討一下libevent實現機制。
struct eventop:對select/pool/epoll/kqueue等底層函數,按照該結構提供的接口方式,封裝接口統一的函數。
struct eventop {
constchar *name;
void*(*init)(struct event_base *);
int(*add)(void *, struct event *);
int(*del)(void*, struct event *);
int(*dispatch)(struct event_base *, void *, struct timeval *);
void(*dealloc)(struct event_base *, void *);
/*set if we need to reinitialize the event base */
intneed_reinit;
};
struct event_base:至關於一個事件池。一個線程一個。使用提供的API,把須要監控的事件結構加入到該事件池中。
struct event_base {
conststruct eventop *evsel; //指向編譯時選擇的一個select/pool/epoll/kqueue接口封裝對象。
void*evbase;
intevent_count; /* counts numberof total events */
intevent_count_active; /* counts number ofactive events */
intevent_gotterm; /* Set to terminateloop */
intevent_break; /* Set toterminate loop immediately */
/*active event management */
structevent_list **activequeues; //活動事件隊列
intnactivequeues;
/*signal handling info */
structevsignal_info sig;
structevent_list eventqueue; //監聽事件隊列
structtimeval event_tv;
structmin_heap timeheap; //定時器時間堆
structtimeval tv_cache;
};
線程事件循環使用底層機制異步監控事件。
struct event:事件結構。
struct event {
TAILQ_ENTRY(event) ev_next;
TAILQ_ENTRY(event) ev_active_next;
TAILQ_ENTRY(event) ev_signal_next;
unsignedint min_heap_idx; /* for managingtimeouts */
structevent_base *ev_base; //事件輸入的evnet_base
intev_fd;
shortev_events;
shortev_ncalls;
short*ev_pncalls; /* Allows deletes incallback */
structtimeval ev_timeout;
intev_pri; /* smaller numbers arehigher priority */
void(*ev_callback)(int, short, void *arg); //回調函數
void*ev_arg;
intev_res; /* result passed toevent callback */
intev_flags;
};
按照使用libevnet庫順序,看一下相關函數作什麼操做。
調用event_base_new,初始化struct event_base對象。
event_base_new裏作了以下工做:
一、 申請內存
二、 初始化定時器堆和事件隊列
三、 爲event_base對象選擇底層事件函數封裝對象。根據編譯選項,初始化eventops全局對象。該對象存放指向底層select/pool/epoll等功能的封裝函數。
四、 初始化活動隊列。
初始化structevent對象。
一、 把參數中指定初始化的事件對象的ev_base指向全局的current_base。
二、 賦值回調函數、描述符、監視事件等變量。
把struct event對象指向的event_base對象賦值爲指定的對象。
event_set函數把event對象的ev_base指向全局的current_base,多線程環境下,如須要用本身的event_base對象,須要調用event_base_set從新指定event_base對象。
增長指定event到監控池裏。
一、 對於讀、寫、信號事件,調用封裝的add函數,調用底層select/pool/epoll相關函數,增長到操做系統事件監控裏。對於epoll,調用的是epoll_add函數。Epoll_add函數調用epoll_ctl添加事件監控,libevent使用水平觸發方式。把監聽時間加入到event_base的事件隊列中。
二、 對應定時器事件,加入到event_base的定時器最小堆裏。
三、 對信號事件,調用evsignal_add,加入事件處理隊列中。
事件循環,事件發生後,調用相應回調函數。
一、 計算最近的超時時間:定時器最小堆按照超時時間排序,取最小的超時時間;如已有活動事件或指定不阻塞,超時時間爲0。
二、 調用dispatch。對epoll,對應epoll_dispatch函數。該函數調用epoll_wait監控指定事件。
三、 把到了超時時間的時間加入到活動事件隊列。從超時時間最小堆中依次取最小超時時間和當前時間比較,對小於/等於當前時間的事件,加入到活動事件隊列。
四、 循環調用活動事件隊列中全部事件的回調函數。
epoll_dispatch:
1. 計算epoll_wait函數須要的超時時間,把時間轉換成微妙。
2. 如epoll_wait被信號中斷,把相應信號對應的事件加入到活動事件隊列。
3. 如監視的描述上發生了特定事件,把相應事件對象加入到活動事件隊列。