所有 I/O 多工模型 (select、poll、epoll、kqueue、IOCP、devpoll) 均實作此介面:
struct eventop {
const char *name; // 後端名稱
void *(*init)(struct event_base *); // 初始化,回傳後端私有資料
int (*add)(void *, struct event *); // 新增事件監控
int (*del)(void *, struct event *); // 移除事件監控
int (*dispatch)(struct event_base *, void *, struct timeval *); // 事件循環核心
void (*dealloc)(struct event_base *, void *); // 釋放資源
int need_reinit; // 需重新初始化旗標
};
struct event_base {
const struct eventop *evsel; // 當前後端
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; // 時間快取
};
struct event {
TAILQ_ENTRY(event) ev_next; // 總隊列鏈結
TAILQ_ENTRY(event) ev_active_next; // 活躍隊列鏈結
TAILQ_ENTRY(event) ev_signal_next; // 信號隊列鏈結
unsigned int min_heap_idx; // 堆索引
struct event_base *ev_base; // 所屬 base
int ev_fd; // 監控檔案描述符
short ev_events; // 事件類型 (EV_READ/EV_WRITE/EV_SIGNAL/EV_TIMEOUT/EV_PERSIST)
short ev_ncalls; // 回調呼叫次數
short *ev_pncalls; // 回調中允許刪除自身
struct timeval ev_timeout; // 絕對超時時間
int ev_pri; // 優先級 (數字越小優先級越高)
void (*ev_callback)(int, short, void *arg); // 回調函式
void *ev_arg; // 回調參數
int ev_res; // 回調結果傳遞
int ev_flags; // 狀態旗標 (EVLIST_INSERTED/ACTIVE/TIMEOUT/INTERNAL)
};
使用 BSD Tail Queue (TAILQ_ENTRY) 實現多重隊列掛載,單執行緒下極簡高效。
struct selectop {
int event_fds; // 最大 fd + 1
int event_fdsz; // fd_set 大小
fd_set *event_readset_in; // 讀集合輸入
fd_set *event_writeset_in; // 寫集合輸入
fd_set *event_readset_out; // 讀集合輸出 (select 修改)
fd_set *event_writeset_out; // 寫集合輸出
struct event **event_r_by_fd; // fd → 讀事件映射
struct event **event_w_by_fd; // fd → 寫事件映射
};
const struct eventop selectops = {
"select",
select_init,
select_add,
select_del,
select_dispatch,
select_dealloc,
0
};
struct event_base *event_base_new(void) {
// ... 記憶體配置與初始化 ...
// 依優先序嘗試初始化後端 (epoll > kqueue > devpoll > poll > select)
for (i = 0; eventops[i] && !base->evbase; i++) {
base->evsel = eventops[i];
base->evbase = base->evsel->init(base); // 呼叫後端 init
}
event_base_priority_init(base, 1); // 預設單一優先級隊列
return base;
}
struct event_base *event_init(void) {
struct event_base *base = event_base_new();
if (base) current_base = base; // 全域變數
return base;
}
int event_add(struct event *ev, const struct timeval *tv) {
struct event_base *base = ev->ev_base;
const struct eventop *evsel = base->evsel;
void *evbase = base->evbase;
// 1. 定時器預留堆空間
if (tv && !(ev->ev_flags & EVLIST_TIMEOUT)) {
if (min_heap_reserve(&base->timeheap, 1 + min_heap_size(&base->timeheap)) == -1)
return -1;
}
// 2. I/O/信號事件加入後端
if ((ev->ev_events & (EV_READ|EV_WRITE|EV_SIGNAL)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
int res = evsel->add(evbase, ev);
if (res != -1) event_queue_insert(base, ev, EVLIST_INSERTED);
}
// 3. 處理定時器
if (res != -1 && tv) {
// 移除舊定時器 (若存在)
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(base, ev, EVLIST_TIMEOUT);
// 處理活躍中定時器重排
if ((ev->ev_flags & EVLIST_ACTIVE) && (ev->ev_res & EV_TIMEOUT)) {
if (ev->ev_ncalls && ev->ev_pncalls) *ev->ev_pncalls = 0;
event_queue_remove(base, ev, EVLIST_ACTIVE);
}
struct timeval now;
gettime(base, &now);
evutil_timeradd(&now, tv, &ev->ev_timeout); // 計算絕對超時時間
event_queue_insert(base, ev, EVLIST_TIMEOUT); // 推入最小堆
}
return res;
}
int event_base_loop(struct event_base *base, int flags) {
struct timeval tv, *tv_p = &tv;
// ... 變數宣告 ...
while (!done) {
timeout_correct(base, &tv); // 校正時間 (非單調時鐘)
if (!base->event_count_active && !(flags & EVLOOP_NONBLOCK)) {
timeout_next(base, &tv_p); // 取最近定時器剩餘時間
} else {
evutil_timerclear(&tv); // 有活躍事件或非阻塞模式 → 立即輪詢
}
if (!event_haveevents(base)) return 1; // 無事件退出
gettime(base, &base->event_tv);
base->tv_cache.tv_sec = 0;
int 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;
}
return 0;
}
void timeout_process(struct event_base *base) {
struct timeval now;
struct event *ev;
if (min_heap_empty(&base->timeheap)) return;
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); // 加入活躍隊列
}
}
eventop 統一封裝 select/epoll/kqueue 等差異,上層邏輯零感知切換。event 同時掛載於總隊列、活躍隊列、定時器堆、信號隊列,狀態旗標 ev_flags 精確追蹤。activequeues[priority] 陣列實現 O(1) 最高優先級取出。timeout_next 計算最近超時傳給 dispatch,超時轉為活躍事件同一分發路徑。tv_cache 減少 gettimeofday 系統呼叫。libevent 以 ~2000 行核心程式碼 實現跨平台高效能事件驅動核心,是網路程式設計的經典教材與生產級基石。