libevent簡單介紹

1      簡介

主頁: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對底層異步函數提供了較薄封裝,庫自己沒有消耗過多性能;另外,使用堆排序管理定時器隊列,提供了較高的性能。網絡

2      使用介紹

2.1   網絡IO

2.1.1  代碼例子

//事件回調處理函數數據結構

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;

}

 

2.1.2  基本函數介紹

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_baseLibevnet內置一個全局的event_base結構。多個線程應用中,若是多個線程都須要一個libevent事件循環,須要調用event_base_set修改事件結構基於的event_base

 

event_add:增長事件到事件監控中。

event_base_loop:事件循環。調用底層的selectpollepoll等,如監聽事件發生,調用事件結構中指定的回調函數。

 

2.2   定時器

2.2.1  代碼例子

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;

}

 

2.2.2  基本函數介紹

evtimer_set: 設置定時器事件。

evtimer_add: 增長定時器時間。

3      源代碼簡介

Libevent在底層select、pool、kqueue和epoll等機制基礎上,封裝出一致的事件接口。能夠註冊可讀、可寫、超時等事件,指定回調函數;當事件發生後,libevent調用回調函數,能夠在回調函數裏實現自定義功能。前面例子已經展示瞭如何使用libevent接口。

 

本節探討一下libevent實現機制。

 

3.1   重要結構體

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;

};

 

3.2   主要函數介紹

按照使用libevnet庫順序,看一下相關函數作什麼操做。

3.2.1  event_init

調用event_base_new,初始化struct event_base對象。

event_base_new裏作了以下工做:

一、 申請內存

二、 初始化定時器堆和事件隊列

三、 爲event_base對象選擇底層事件函數封裝對象。根據編譯選項,初始化eventops全局對象。該對象存放指向底層select/pool/epoll等功能的封裝函數。

四、 初始化活動隊列。

 

3.2.2  event_set

初始化structevent對象。

一、 把參數中指定初始化的事件對象的ev_base指向全局的current_base。

二、 賦值回調函數、描述符、監視事件等變量。

 

3.2.3  event_base_set

把struct event對象指向的event_base對象賦值爲指定的對象。

event_set函數把event對象的ev_base指向全局的current_base,多線程環境下,如須要用本身的event_base對象,須要調用event_base_set從新指定event_base對象。

 

3.2.4  event_add

增長指定event到監控池裏。

一、 對於讀、寫、信號事件,調用封裝的add函數,調用底層select/pool/epoll相關函數,增長到操做系統事件監控裏。對於epoll,調用的是epoll_add函數。Epoll_add函數調用epoll_ctl添加事件監控,libevent使用水平觸發方式。把監聽時間加入到event_base的事件隊列中。

二、 對應定時器事件,加入到event_base的定時器最小堆裏。

三、 對信號事件,調用evsignal_add,加入事件處理隊列中。

 

3.2.5  event_base_loop

事件循環,事件發生後,調用相應回調函數。

一、 計算最近的超時時間:定時器最小堆按照超時時間排序,取最小的超時時間;如已有活動事件或指定不阻塞,超時時間爲0。

二、 調用dispatch。對epoll,對應epoll_dispatch函數。該函數調用epoll_wait監控指定事件。

三、 把到了超時時間的時間加入到活動事件隊列。從超時時間最小堆中依次取最小超時時間和當前時間比較,對小於/等於當前時間的事件,加入到活動事件隊列。

四、 循環調用活動事件隊列中全部事件的回調函數。

 

epoll_dispatch:

1.      計算epoll_wait函數須要的超時時間,把時間轉換成微妙。

2.      如epoll_wait被信號中斷,把相應信號對應的事件加入到活動事件隊列。

3.      如監視的描述上發生了特定事件,把相應事件對象加入到活動事件隊列。

相關文章
相關標籤/搜索