1. 程式人生 > >libevent事件處理框架分析

libevent事件處理框架分析

這兩天大致看了看libevent的程式碼,簡單做一個分析.

libevent最大的特點就是封裝了對以下三種事件的響應:IO事件,定時器事件,訊號事件.這裡就分析libevent如果做到這一點的,在libevent中還包括一些其他的功能(如緩衝區),但是我這裡就重點講解這一部分了.

事件原型,簡單看一看用於封裝事件的結構體定義:
structevent {
    TAILQ_ENTRY (
event) ev_next;
    TAILQ_ENTRY (
event) ev_active_next;
    TAILQ_ENTRY (
event) ev_signal_next;
    unsigned 
int min_heap_idx;    
/* for managing timeouts */struct event_base *ev_base;

    
int ev_fd;
    
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)(intshortvoid*arg);
    
void*
ev_arg;

    
int ev_res;        /* result passed to event callback */int ev_flags;
};
其中的ev_callback就是回撥函式,也就是說當所關注的事件發生時所要觸發的函式是註冊到這個函式指標中的.

1)IO事件:再簡單不過了,對select/epoll/poll等之類的呼叫進行封裝即可,所提供的介面無非這幾種:
struct eventop {
    
constchar*name;
    
void*(*init)(struct event_base *);
    
int (*add)(void*structevent
*);
    
int (*del)(void*structevent*);
    
int (*dispatch)(struct event_base *void*struct timeval *);
    
void (*dealloc)(struct event_base *void*);
    
/* set if we need to reinitialize the event base */int need_reinit;
};
在我看過的很多開源伺服器原始碼(如lighttpd)中都有類似的封裝,不是什麼新鮮的東西.

2)定時器事件:libevent採用堆資料結構存放所要定時的事件的時間,大家知道堆可以用來實現優先佇列,在這裡,所有的定時器就放在這樣的一個數據結構中了.

3)訊號事件:所有的訊號都註冊回撥函式為evsignal_handler(在signal.c中),這個函式的功能就是在某訊號被觸發的時候將該訊號被觸發的計數器加1,同時置一個標誌位表示有訊號被觸發.

現在,把所有這些結合起來,看看libevent框架的主迴圈是如何工作的,用簡單的偽碼錶示:
主迴圈
      更新當前時間

      將當前時間與存放時間的堆中的時間依次進行比較,由於是採用堆實現的,這裡查詢相當的快,於是所有可以被觸發的定時器事件都從堆中被取出,同時取下的事件被放到一個活動事件的佇列中

      呼叫封裝IO操作的dispatch函式,在其中也將被觸發的IO事件加入到那個存放活動事件的佇列中

      在dispatch的函式中如果訊號被觸發的標誌位被置位,說明有訊號被觸發,呼叫evsignal_process函式,這個函式的功能也是把所有被觸發的事件放到活動事件的佇列中

       好了,現在所有可以被觸發的事件都在活動事件佇列中了,依次遍歷取出來呼叫它們註冊的回撥函式就成了.



上面就是libevent處理這三種事件的大體框架.

說一說我認為這個框架存在的缺點:
1) callback函式只能有一個,假設這樣一個場景,我需要對某個連線socket同時監控它的可讀/可寫/超時事件,那麼我需要針對同一個socket fd生成三個event物件.

2) 在主迴圈中,每次都要去查詢存放時間的堆看看有沒有定時器事件可以被觸發,問題在於,很多時候,一個主迴圈很快就到了下一次,而時間過去的並不多,這次去檢查時間是冗餘的操作,當然了,由於libevent的定時器是精確到毫秒級別的,所以有這麼做的必要,但是在一個真正的伺服器中,我懷疑有多少需要精確到微秒級別的事件,所以呢,我覺得這個可以做一個改進,每次更新時間之後跟上一次更新的時間做一個比較,如果超過了一秒(或者把這個間隔改成可以由使用者配置的)再去檢查堆上面的時間.