libevent解讀一

一、接觸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事件的集成。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章