1. 程式人生 > >libevent高效能網路庫原始碼分析——事件(event)及其介面(三)

libevent高效能網路庫原始碼分析——事件(event)及其介面(三)

libevent的結構

event結構

// include/event2/event_struct.h
struct event {
    TAILQ_ENTRY(event) ev_active_next; // 已就緒的事件連結串列
    TAILQ_ENTRY(event) ev_next; // 已註冊的事件連結串列
    /* for managing timeouts */
    union {
        TAILQ_ENTRY(event) ev_next_with_common_timeout;
        int min_heap_idx; // 事件在堆對應的陣列下標
} ev_timeout_pos; evutil_socket_t ev_fd; //對於I/O事件,是繫結的檔案描述符;對於signal事件,是繫結的訊號 struct event_base *ev_base; //指向事件框架例項 union { /* used for io events */ struct { TAILQ_ENTRY(event) ev_io_next; struct timeval ev_timeout; } ev_io; /* used by signal events */
struct { TAILQ_ENTRY(event) ev_signal_next; short ev_ncalls; //事件就緒執行時,呼叫ev_callback的次數 /* Allows deletes in callback */ short *ev_pncalls; //指標,通常指向 ev_ncalls 或者為 NULL } ev_signal; } _ev; short ev_events; //event的事件型別 short ev_res; //事件的回撥函式的執行結果
short ev_flags; //用於標記 event資訊的欄位,表明事件當前的狀態 ev_uint8_t ev_pri; //事件的優先順序設定,值越小,則優先順序越高 ev_uint8_t ev_closure; // struct timeval ev_timeout; //超時設定 /* allows us to adopt for different types of events */ void (*ev_callback)(evutil_socket_t, short, void *arg); void *ev_arg; };

其中主要欄位說明如下:

1 . ev_next,ev_active_next 和 ev_signal_next 都是雙向連結串列節點指標;通過雙向連結串列對不同型別、不同狀態的事件進行管理。libevent 使用雙向連結串列儲存所有註冊的 I/O和 Signal 事件,ev_next 指向“已註冊事件連結串列”,其包含了所有的已註冊的事件; ev_io_next 指向I/O事件在I/O事件連結串列中的位置; ev_signal_next 就是 signal 事件在 signal 事件連結串列中的位置; ev_active_next指向已啟用的事件在active連結串列中的位置。


這裡寫圖片描述

2 . ev_events
即event註冊的事件型別

#define EV_TIMEOUT    0x01 // 定時事件
#define EV_READ       0x02 // IO事件讀
#define EV_WRITE      0x04 // IO事件寫
#define EV_SIGNAL     0x08 // 訊號事件
#define EV_PERSIST    0x10 // 表明是一個永久事件

3 . ev_flags
某個event可在多個佇列中,通過標誌來標記event的當前所在的位置,狀態值:

#define EVLIST_TIMEOUT  0x01 //event在min_heap中
#define EVLIST_INSERTED 0x02 //event在已註冊的事件連結串列中
#define EVLIST_SIGNAL   0x04 //event在event_signal_map中
#define EVLIST_ACTIVE   0x08 //event在啟用的連結串列中
#define EVLIST_INTERNAL 0x10 //內部使用標記
#define EVLIST_INIT     0x80 //event已經初始化

注:event_io_map 容器沒有專門標誌, 如果 ev_event 有 EV_READ|EV_WRITE, event_add 後一定在 event_io_map 中。且EV_READ|EV_WRITE不能和signal同時出現。

4 . 其他

  • min_heap
    基於小根堆管理超時event,其中min_heap[0] 存放在第一個(最快)要超時的 event (的指標).
  • event_io_map
    一個 hashtable, key是fd, value是fd對應的事件list. 如果對一個fd監控可讀或可寫時間的話, 那麼這個fd繫結的 event 就會被加入到 value 的list 中. 同理如果 event_del 一個監控可讀|可寫事件的event, 那麼也會從 key(fd)->value->list 中刪除對應事件.
  • event_signal_map
    與event_io_map 相似,也可以認為是一個 hashtable, key 是 signal number(signo), value 是這個 signo 對應的 event list.(一個訊號可以對應多個event, 訊號發生後會挨個呼叫這些event 的回撥).

event相關介面

1 . 建立事件 (event_new)
主要完成event的空間分配,

struct event * event_new(struct event_base *base,
                        evutil_socket_t fd,
                        void (*cb)(evutil_socket_t, short, void *),
                        void *arg)
{
    struct event *ev;
    // 分配new event的空間
    ev = mm_malloc(sizeof(struct event));
    ...
    // event的引數賦值
    ev->ev_base = base;         // 依附的event_base
    ev->ev_callback = callback; // 設定回撥函式
    ev->ev_arg = arg;
    ev->ev_fd = fd;
    ev->ev_events = events;     // 該event註冊的事件型別
    ev->ev_res = 0;
    ev->ev_flags = EVLIST_INIT; // ev_flags設定為初始化
    ev->ev_ncalls = 0;          // 回撥函式執行了0次
    ev->ev_pncalls = NULL;

    // 根據event的ev_events的值,設定event的關閉方式
    if (events & EV_SIGNAL) {...}
    else 
    {
        if (events & EV_PERSIST) { // event為
            evutil_timerclear(&ev->ev_io_timeout);  // 清除超時設定
            ev->ev_closure = EV_CLOSURE_PERSIST;
        } else {
            ev->ev_closure = EV_CLOSURE_NONE;
        }
    }

    ...
    // 優先順序設定為active queue的大小的中間值
    ev->ev_pri = base->nactivequeues / 2;
    ...
    return (ev);
}

2 . 事件設定
對event的設定主要通過三個函式完成:event_set()、event_base_set()、event_priority_set()。

  • event_set函式的實現與event_new的類似,對event結構體的各引數進行賦值。
void event_set(struct event *ev,
                int fd, 
                short events, 
                void (*callback)(int, short, void *), // event的回撥函式
                void *arg) // 使用者傳遞的引數
  • event_base_set函式完成event註冊到其他的event_base,僅當event的狀態為EVLIST_INIT(即只執行了event_new,但還沒有event_add)時,才能將event註冊到新的event_base。
int event_base_set(struct event_base *base, struct event *ev)
  • event_priority_set函式設定event的執行優先順序,僅當event的狀態不是EVLIST_ACTIVE,才能將event的優先順序進行設定。
int event_priority_set(struct event *ev, int pri)