libev ev_io源碼分析

最近用libev做項目,在使用libev時遇到了一些問題,最近看了libev的代碼,整理了一部分libev實現。

1. ev_watcher和ev_loop

ev_watcher:

libev中所有事件的基礎類型ev_watcher,所有的事件都可以通過(W)watcher轉換成ev_watcher,大寫的W在libev定義的是ev_watcher *。抽象出ev_watcher作用是所有的watcher都可以共用ev_start、ev_stop函數

下面用ev_TYPE表示ev_io、ev_timer、ev_async各種watcher類型的通用形式。各種類型的ev_TYPE都繼承自ev_watcher,還有形如ev_TYPE_init、ev_TYPE_start、ev_TYPE_stop。

typedef struct ev_watcher {
    int active;
    int pending;
    int priority;
    void *data;
    void (*cb)(struct ev_loop *loop, struct ev_watcher *w, int revents);
} ev_watcher;
typedef ev_watcher *W

active: 標記當前的watcher有沒有active,執行ev_TYPE_start之後watcher算是active
pending: 大於0表示當前的watcher有時間觸發,當前的watcher正在loop->pendings隊列裏(loop->pendings隊列是一個數組),pending的值是loop->pendings隊列數組中的下標。 priority: watcher的優先級
data: 在初始化watcher時,上層應用用於保存cb回調時候將要用到的數據或結構體pending隊列數組中的下標。
cb: 對pending狀態的watcher執行的回調函數。

ev_loop:

在一個事件循環的週期裏,ev_loop保存我們所有watcher所需的信息。包含各種watcher鏈表、數組、執行的狀態信息,epoll相關的數據結構,時間等。

loop執行流程:

2. ev_io

和ev_io相關的結構體

// ev_watcher_list可以說成是繼承ev_watcher,ev_watcher_list是爲了有多個watcher的時候,可以把ev_watcher用鏈表的時候連起來
// 抽象出ev_watcher_list作用是所有的watcher都可以共用WL鏈表,包括wlist_add、wlist_del的操作

typedef struct ev_watcher_list {
    int active;
    int pending;
    int priority;
    void *data;
    void (*cb)(struct ev_loop *loop, struct ev_watcher_list *w, int revents);
    struct ev_watcher_list *next;
} ev_watcher_list;
typedef ev_watcher_list *WL;

// ev_io繼承ev_watcher_list,ev_io事件鏈表中真正保存的是ev_io

typedef struct ev_io {
    int active;
    int pending;
    int priority;
    void *data;
    void (*cb)(struct ev_loop *loop, struct ev_io *w, int revents);
    struct ev_watcher_list *next;

    int fd;
    int events;
} ev_io;

// 用於保存fd 事件信息的結構,loop初始化的時候會初始化一個ANFD的數組,每個ANFD表示一個fd對應的這個fd的所有事件信息

typedef struct {
    WL head; // 每個fd可以有多個事件
    unsigned char events; // 事件類型
    unsigned char reify;
    unsigned char emask;
    unsigned char cdel;

    unsigned int egen;
} ANFD;

ev_loop結構中對應ev_io關鍵的變量
ANFD *anfds; // 每個fd對應一個ANFD[fd]結構,添加的watcher都保存在對應的anfds[fd]結構中。
ANPENDING *pendings [5]; //
struct epoll_event *epoll_events; //
int *fdchanges; // 執行ev_io_start用fdchanes記錄fd對應的anfds[fd]有修改。

ev_io watcher添加、執行、刪除

1)向fd添加watcher:

ev_io_init // 只做一些初始化的操作
ev_io_start // 添加的新的事件,沒有做真正的事件監聽的改變(沒執行epoll_ctl)
在ev_io_start會執行
wlist_add (&((loop)->anfds)[fd].head, (WL)w); // 把watcher加到anfds[fd]對應的事件鏈表中
fd_change (loop, fd, ((w->events & EV__IOFDSET) | 1)); // 把fd添加到fdchanges數組裏
。。。 中間可能會執行很多操作,可以停掉事件等等 。。。
在每次epoll_wait之前執行fd_reify(loop) // 這裏纔會真正調用epoll_ctl
在fd_reify中會遍歷fdchanges數組,把對fd事件的修改通過調用epoll_modify來做真正的修改

2)喚醒watcher:執行loop->backend_poll調用epoll_wait,有read或者寫的事件返回

根據epoll_wait返回的fd,找到anfd,遍歷anfd->head(head是保存我們加入的事件鏈表),如果匹配返回的事件的類型,把watcher加入到pendings數組裏

3)回調watcher:執行loop->invoke_cb(對應ev_invoke_pending函數)

在invoke_cb中遍歷loop->pendings數組,找到對應的watcher進行回調(執行watcher->cb)

3)刪除watcher:執行ev_io_stop

刪除loop->anfds[fd]->head鏈表中對應的watcher,把fd添加到fdchanges數組裏,等待下次執行fd_reify(loop)真正刪除

這裏是根據libev源代碼,處理的ev_io事件代碼,方便看libev的實現。

參考資料:

libev官方使用文檔 http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod

libev設計分析 http://cnodejs.org/topic/4f16442ccae1f4aa270010a3

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章