event_base 是 libevent 事件循環的核心上下文,管理所有事件、優先級隊列、定時器堆、信號處理等:
struct event_base {
const struct eventop *evsel; // 事件驅動後端 (select/epoll/kqueue...)
void *evbase; // 後端私有資料
int event_count; // 總事件數
int event_count_active; // 活躍事件數
int event_gotterm; // 正常終止旗標
int event_break; // 立即終止旗標
struct event_list **activequeues; // 活躍事件優先級隊列陣列
int nactivequeues; // 優先級數量
struct evsignal_info sig; // 信號處理資訊
struct event_list eventqueue; // 所有註冊事件隊列
struct timeval event_tv; // 事件時間
struct min_heap timeheap; // 定時器最小堆
struct timeval tv_cache; // 時間快取
};
int event_base_loop(struct event_base *base, int flags) {
// ... 省略變數宣告 ...
timeout_correct(base, &tv); // 校正時間 (處理非單調時鐘)
if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
timeout_next(base, &tv_p); // 無活躍事件時,取得最近定時器超時時間
} else {
evutil_timerclear(&tv); // 有活躍事件則立即輪詢 (tv=0)
}
base->tv_cache.tv_sec = 0;
res = evsel->dispatch(base, evbase, tv_p); // 呼叫後端 dispatch (如 select)
if (res == -1) return -1;
gettime(base, &base->tv_cache);
timeout_process(base); // 處理超時事件 → 移入活躍隊列
if (base->event_count_active) {
event_process_active(base); // 執行活躍事件回調
if (!base->event_count_active && (flags & EVLOOP_ONCE))
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
// ... 迴圈結束判斷 ...
}
static int timeout_next(struct event_base *base, struct timeval **tv_p) {
struct timeval now;
struct event *ev = min_heap_top(&base->timeheap);
if (!ev) { *tv_p = NULL; return 0; } // 無定時器,永久等待 I/O
if (gettime(base, &now) == -1) return -1;
if (evutil_timercmp(&ev->ev_timeout, &now, <=)) { // 已超時
evutil_timerclear(tv);
return 0;
}
evutil_timersub(&ev->ev_timeout, &now, tv); // 剩餘時間 = 超時時間 - 現在
return 0;
}
從最小堆取出最早超時事件,計算剩餘時間傳給 dispatch() 作為最大等待時間。
void timeout_process(struct event_base *base) {
struct timeval now;
gettime(base, &now);
while ((ev = min_heap_top(&base->timeheap))) {
if (evutil_timercmp(&ev->ev_timeout, &now, >)) break; // 未超時,結束
event_del(ev); // 從 I/O 隊列移除 (含定時器堆)
event_active(ev, EV_TIMEOUT, 1); // 加入活躍隊列
}
}
遍歷最小堆,將所有已超時事件從堆中刪除並插入活躍隊列。注意:event_del() 會移除定時器,若需重複觸發,回調中需重新 event_add()。
void event_queue_insert(struct event_base *base, struct event *ev, int queue) {
ev->ev_flags |= queue;
switch (queue) {
case EVLIST_INSERTED:
TAILQ_INSERT_TAIL(&base->eventqueue, ev, ev_next); // 總事件隊列
break;
case EVLIST_ACTIVE:
base->event_count_active++;
TAILQ_INSERT_TAIL(base->activequeues[ev->ev_pri], ev, ev_active_next); // 對應優先級隊列
break;
case EVLIST_TIMEOUT:
min_heap_push(&base->timeheap, ev); // 推入最小堆
break;
}
}
使用 Tail Queue 實現隊列。活躍隊列為指標陣列 activequeues[priority],優先級數字越小優先級越高。
static void event_process_active(struct event_base *base) {
struct event *ev;
struct event_list *activeq = NULL;
// 尋找最高優先級非空隊列
for (int i = 0; i < base->nactivequeues; ++i) {
if (TAILQ_FIRST(base->activequeues[i])) {
activeq = base->activequeues[i];
break;
}
}
// 依序執行該優先級隊列所有事件
for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) {
if (ev->ev_events & EV_PERSIST)
event_queue_remove(base, ev, EVLIST_ACTIVE); // 持久事件僅移出活躍隊列
else
event_del(ev); // 一次性事件完全移除
short ncalls = ev->ev_ncalls;
ev->ev_pncalls = &ncalls;
while (ncalls--) {
ev->ev_ncalls = ncalls;
(*ev->ev_callback)((int)ev->ev_fd, ev->ev_res, ev->ev_arg); // 呼叫回調
if (event_gotsig || base->event_break) return; // 信號/中斷檢查
}
}
}
嚴格按優先級順序處理。持久事件 (EV_PERSIST) 執行後保留於系統,非持久事件執行後銷毀。回調可重複呼叫 ev_ncalls 次。
activequeues 陣列實現 O(1) 最高優先級事件取出。timeout_next 計算最近超時時間傳給 select/epoll,超時即轉為活躍事件統一處理。tv_cache 減少系統呼叫 gettimeofday 開銷。此架構支撐了 libevent 高效能、可擴充的事件驅動核心,是 Nginx、Memcached、Chromium 等高效能專案的基石。