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

libevent 源碼淺析 (一):核心資料結構與事件循環架構全景解析

     編輯:關於C++

libevent 核心三大資料結構

1. eventop:事件後端抽象介面

所有 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;                     // 需重新初始化旗標
};

2. event_base:事件循環上下文

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

3. event:單一事件實體

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) 實現多重隊列掛載,單執行緒下極簡高效。

Select 後端實作範例

selectop:Select 後端私有資料

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 → 寫事件映射
};

selectops:Select 後端介面實作


const struct eventop selectops = {
    "select",
    select_init,
    select_add,
    select_del,
    select_dispatch,
    select_dealloc,
    0
};

關鍵函式流程

1. event_base_new():建立事件循環上下文


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;
}

2. event_init():全域預設 base


struct event_base *event_init(void) {
    struct event_base *base = event_base_new();
    if (base) current_base = base;  // 全域變數
    return base;
}

3. event_add():註冊事件


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;
}

4. event_base_loop():事件循環核心


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;
}

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


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);  // 加入活躍隊列
    }
}

架構設計精華

  1. 後端抽象介面:eventop 統一封裝 select/epoll/kqueue 等差異,上層邏輯零感知切換。
  2. 多隊列事件管理:單一 event 同時掛載於總隊列、活躍隊列、定時器堆、信號隊列,狀態旗標 ev_flags 精確追蹤。
  3. 優先級調度:activequeues[priority] 陣列實現 O(1) 最高優先級取出。
  4. 定時器與 I/O 統一:timeout_next 計算最近超時傳給 dispatch,超時轉為活躍事件同一分發路徑。
  5. 時間快取:tv_cache 減少 gettimeofday 系統呼叫。
  6. 單執行緒無鎖:Tail Queue + 簡單旗標,極致效能。

libevent 以 ~2000 行核心程式碼 實現跨平台高效能事件驅動核心,是網路程式設計的經典教材與生產級基石。

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