通用結構體
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* previous element */ \
}
放置在event結構體中,將event串成隊列。
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* 隊列頭*/ \
struct type **tqh_last; /* 隊列尾 */ \
}
event_base
event_base 代表一個libevent實體。其結構如下:
struct event_base {
const struct eventop *evsel; /* evbase對應的操作函數 */
void *evbase; /* 採用何種IO多路複用函數 */
int event_count; /* 事件總量 */
int event_count_active; /* 激活事件數量 */
int event_gotterm; /* 優雅的結束,處理完激活事件隊列中事件後結束 */
int event_break; /* 立即結束 */
struct event_list **activequeues; /* 多個激活事件隊列 */
int nactivequeues; /* 隊列個數,0最高優先級 */
struct evsignal_info sig; /* 信號結構體 */
struct event_list eventqueue; /* 信號/IO事件隊列 */
struct min_heap timeheap; /* 時間堆 */
struct timeval tv_cache; /* 緩存時間 */
struct timeval event_tv; /* 事件時間 */
};
1. evsel 與 evbase
evsel 代表操作集合。
const struct eventop epollops = {
"epoll",
epoll_init,
epoll_add,
epoll_del,
epoll_dispatch,
epoll_dealloc,
1 /* need reinit */
};
evbase 代表採用哪種IO多路複用函數, epoll, select, poll等。struct epollop {
struct evepoll *fds;
int nfds;
struct epoll_event *events;
int nevents;
int epfd;
};
上述兩者配對使用,evbase 相當於C++的this指針,而epollops內的函數相當於類的成員函數。
2. activequeues
activequeues隊列是一個指針數組,數組中每個元素可以指向一個事件隊列;數組索引表示優先級,0代表最高優先級。
當處理事件時候,從頭掃描數組,若發現非空事件隊列,進行處理,只有當前優先級隊列處理完成後,才進行下一優先級的處理。
3. sig、eventqueue、timeheap
上述三個變量分別用於處理信號事件,信號與IO事件隊列,定時器事件小根堆。
4. tv_cache 、 event_tv
由於獲取時間需要調用系統調用,這是一個耗時操作,因此libevent將時間緩存,減少系統調用次數,從而達到加速的目的。
event_tv 用於修正時間,後續時間管理章節說明。
event
struct event {
TAILQ_ENTRY (event) ev_next; /* IO、信號事件結點 */
TAILQ_ENTRY (event) ev_active_next; /* 激活事件結點 */
TAILQ_ENTRY (event) ev_signal_next; /* 信號事件結點 */
unsigned int min_heap_idx; /* 時間堆中索引位置 */
struct event_base *ev_base; /* 指向event_base 結構體 */
int ev_fd; /* 用戶套接字 或 信號*/
short ev_events; /* 事件類型,可讀,可寫,持久,超時 */
short ev_ncalls; /* 事件回調函數需要被調用次數 */
short *ev_pncalls; /* Allows deletes in callback */
struct timeval ev_timeout; /* 超時時刻 */
int ev_pri; /* 事件優先級,0最高優先級 */
void (*ev_callback)(int, short, void *arg);/* 事件回調函數 */
void *ev_arg; /* 回調函數參數 */
int ev_res; /* result passed to event callback */
int ev_flags; /* 事件目前狀態, TIMEOUT, INIT, SIGNAL */
};
1. ev_next、ev_active_next、ev_singal_next、min_head_idx
上述四個變量比較易於理解,分別是事件在不同隊列中的一個節點。有一個奇怪點,信號事件既在ev_next中,
又在ev_signal_next中。當用戶添加信號事件的時候,會在事件隊列中添加,同時,也會在該信號對應的隊列中添加。
允許同一信號,註冊不同的事件,這樣signal[sig no] 將會掛一個鏈表,如下圖。詳細內容在信號一節中說明。
j
(圖片來自互聯網)
2. ev_events
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10 /* Persistant event */
在添加事件的時候指定事件類型,如果非持久事件,則事件在處理後被刪除。3. ev_flags
#define EVLIST_TIMEOUT 0x01
#define EVLIST_INSERTED 0x02
#define EVLIST_SIGNAL 0x04
#define EVLIST_ACTIVE 0x08
#define EVLIST_INTERNAL 0x10
#define EVLIST_INIT 0x80
當前事件在內部的狀態,是否已經超時,是否已經插入事件隊列等等。4. ev_ncalls
運行一個事件的回調函數被調用ev_ncalls次, 主要是用來記錄信號,即同一事件對應的信號在一定時間內發生了n次,
則調用該事件回調函數n次。
5. ev_timeout
如果該事件爲一個超時事件,那麼ev_timeout記錄該事件超時的時間點。比如當前時間是1:40, 那麼用戶指定10分鐘後超時,
則ev_timeout爲1:50.
6. ev_res
該參數傳遞給用戶註冊的事件回調函數,告訴用戶,該事件是由於響應了什麼事件而被激活的。例如:
if (evread != NULL)
event_active(evread, EV_READ, 1);
在epoll中,若一個事件可讀,通過event_active將事件加入激活隊列中去,此時,EV_READ表示該事件激活是由於可讀。
可見ev_res的取值內容同ev_events一致。
void
event_active(struct event *ev, int res, short ncalls)
{
ev->ev_res = res;
event_queue_insert(ev->ev_base, ev, EVLIST_ACTIVE);
}
當處理該事件的時候,ev_res傳遞給用戶回調函數。
while (ncalls) {
ncalls--;
ev->ev_ncalls = ncalls;
(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg);
if (base->event_break)
return;
}
插一句:上述可以看到如果event_break爲真,立即返回,即使激活事件隊列中還有激活事件,也不在進行處理了。