一、接觸libevent
libevent是用C語言編寫的開源高性能網絡庫,是學習網絡編程、後臺開發必備的源碼學習資料。其源代碼相當精煉、易讀,設計技巧和技術框架也很精彩,所以在此我也將我的理解與思考與大家分享,後期我自己也模仿實現了C++版本的網絡庫,歡迎一起來討論。
二、libevent特點
-高性能,體現在使用了事件驅動的設計模式,即Reactor模型。
-跨平臺,支持目前幾乎所有操作系統。
-支持多種I/O多路複用技術epoll、poll、select、kqueue等,並在此基礎上進行封裝,提供統一接口。
-將I/O、定時器、信號三者事件集成統一。
三、Reactor模式
提到網絡編程,不得不提主流的也是使用最廣泛的Reactor設計模式(另一個是Preactor模式),那麼什麼是Reactor,翻譯過來是“反應堆”,它不同於傳統的順序執行邏輯,而是運用了回調函數的技術,應用程序將相應的接口註冊到Reactor上,如果有相應的事件發生,Reactor將主動調用應用程序註冊的接口來實現功能。舉個例子:用手機訂外賣,訂完可以幹其他事,外賣好了自然有騎手送上門,也就意味着可以開吃了,而不用去一遍遍去諮詢商家外賣是否做好。如下圖是Reactor模型的架構圖。
其中,Reactor作爲所有事件的管理組件,支持事件的註冊與註銷,內部通過調用操作系統的I/O多路複用技術進行事件循環,當有事件到達時,調用相應的Event Handler。
那麼libevent的事件處理流程是怎樣的?
1)由應用程序初始化event,設置句柄、事件類型和回調函數;
2)向libevent註冊該事件event,libevent會將I/O事件和Signal事件放入到等待鏈表(雙向鏈表)中進行管理,對於定時事件,libevent使用小根堆來管理。
3)程序調用event_base_dispatch()函數進入無限循環,等待事件就緒。libevent會將所有就緒事件放入到激活鏈表中,最終調用事件的回調函數執行事件處理。
四、核心代碼
1.event
struct event {
/*TAILQ_ENTRY是宏定義的雙向鏈表節點結構
ev_next和ev_signal_next分別I/O事件和信號事件在鏈表中的位置
ev_active_next指向該事件在激活事件鏈表中的位子*/
TAILQ_ENTRY (event) ev_next;
TAILQ_ENTRY (event) ev_active_next;
TAILQ_ENTRY (event) ev_signal_next;
//min_heap_idx表示該事件在小根堆中的索引
unsigned int min_heap_idx; /* for managing timeouts */
struct event_base *ev_base;
int ev_fd; //表示文件描述符或信號
/*事件類型,EV_WRITE\EV_READ\EV_TIMEOUT\EV_SIGNAL\EV_PERSIST
可通過|或運算組合*/
short ev_events;
short ev_ncalls;
short *ev_pncalls; /* Allows deletes in callback */
struct timeval ev_timeout;
int ev_pri; /* smaller numbers are higher priority */
void (*ev_callback)(int, short, void *arg); //回調函數
void *ev_arg;
int ev_res; /* result passed to event callback */
int ev_flags; //標記event當前狀態
};
2、event_base
struct event_base {
const struct eventop *evsel; //指向全局變量static const struct eventop *eventops[]中的一個
void *evbase; //eventop實例對象
int event_count; /* counts number of total events */
int event_count_active; /* counts number of active events */
int event_gotterm; /* Set to terminate loop */
int event_break; /* Set to terminate loop immediately */
/* active event management */
struct event_list **activequeues; //二級指針——activequeues[priority]
int nactivequeues; //就緒事件個數
/* signal handling info */
struct evsignal_info sig;
struct event_list eventqueue;
struct timeval event_tv;
struct min_heap timeheap;
struct timeval tv_cache;
};
static const struct eventop *eventops[] = {
#ifdef HAVE_EVENT_PORTS
&evportops,
#endif
#ifdef HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef HAVE_EPOLL
&epollops,
#endif
#ifdef HAVE_DEVPOLL
&devpollops,
#endif
#ifdef HAVE_POLL
&pollops,
#endif
#ifdef HAVE_SELECT
&selectops,
#endif
#ifdef WIN32
&win32ops,
#endif
NULL
};
for (i = 0; eventops[i] && !base->evbase; i++) {
base->evsel = eventops[i];
base->evbase = base->evsel->init(base);
}
可以看出,libevent將系統提供的I/O demultiplex機制統一封裝成了eventop結構,它的成員是一系列的函數指針,負責完成自身初始化、銷燬釋放;對事件的註冊、註銷和分發
在編譯階段,libevent會根據操作系統按執行效率選擇所支持的複用機制,如Linux下的epoll、poll、select等。
在event_base對象初始化時,會將evsel指向eventops中的一個並實例化一個該對象。3.事件循環
libevent的事件主循環通過event_base_loop()函數完成,其流程如下圖所示。
對應得代碼如下
int event_base_loop(struct event_base *base, int flags)
{
const struct eventop *evsel = base->evsel;
void *evbase = base->evbase;
struct timeval tv;
struct timeval *tv_p;
int res, done;
/* 清空事件緩存 */
base->tv_cache.tv_sec = 0;
if (base->sig.ev_signal_added)
evsignal_base = base;
done = 0;
/* 事件主循環 */
while (!done) {
/* Terminate the loop if we have been asked to */
if (base->event_gotterm) {
base->event_gotterm = 0;
break;
}
if (base->event_break) {
base->event_break = 0;
break;
}
timeout_correct(base, &tv); //校正系統時間
/* 根據timer heap中事件的最小超時時間,設置I/O demultiplexer最大等待時間 */
tv_p = &tv;
if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
timeout_next(base, &tv_p);
} else {
/*
* if we have active events, we just poll new events
* without waiting.
*/
evutil_timerclear(&tv);
}
/* If we have no events, we just exit */
if (!event_haveevents(base)) {
event_debug(("%s: no events registered.", __func__));
return (1);
}
/* update last old time */
gettime(base, &base->event_tv);
/* clear time cache */
base->tv_cache.tv_sec = 0;
/*調用系統I/O demultiplexer等待就緒事件
evsel->dispatch會將就緒事件插入到激活鏈表中*/
res = evsel->dispatch(base, evbase, tv_p);
if (res == -1)
return (-1);
gettime(base, &base->tv_cache);
timeout_process(base);
//根據優先級處理就緒事件
if (base->event_count_active) {
event_process_active(base);
if (!base->event_count_active && (flags & EVLOOP_ONCE))
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
}
/* clear time cache */
base->tv_cache.tv_sec = 0;
event_debug(("%s: asked to terminate loop.", __func__));
return (0);
}
五、總結
這篇文章主要介紹總結了libevent的整體框架與Reactor模型,也從代碼層面分析了libevent主要數據結構與執行邏輯。下一篇會介紹libevent事件的集成。