首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

libevent 源碼淺析 (四):事件循環核心機制與優先級隊列深度解析

     編輯:關於C++

event_base 核心結構體

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;          // 時間快取
};

事件循環核心流程:event_base_loop()

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;
    // ... 迴圈結束判斷 ...
}

關鍵子流程解析

1. 取得最近超時時間:timeout_next()

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() 作為最大等待時間。

2. 處理超時事件:timeout_process()

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()。

3. 活躍事件隊列插入:event_queue_insert()

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],優先級數字越小優先級越高。

4. 執行活躍事件:event_process_active()

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 次。

核心設計精華總結

  1. 多級隊列優先級調度:activequeues 陣列實現 O(1) 最高優先級事件取出。
  2. 定時器與 I/O 統一調度:timeout_next 計算最近超時時間傳給 select/epoll,超時即轉為活躍事件統一處理。
  3. 最小堆高效定時器:插入/刪除/取最小均為 O(log n),適合大量定時器場景。
  4. Tail Queue 無鎖隊列操作:單執行緒事件循環下極簡高效。
  5. 時間快取機制:tv_cache 減少系統呼叫 gettimeofday 開銷。

此架構支撐了 libevent 高效能、可擴充的事件驅動核心,是 Nginx、Memcached、Chromium 等高效能專案的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved