libevent高效能網路庫原始碼分析——事件(event)及其介面(三)
阿新 • • 發佈:2019-02-03
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)