libev 源碼淺析

libev是一個開源的事件驅動庫,基於epoll,kqueue等OS提供的基礎設施。其以高效出名,它能夠將IO事件,定時器,和信號統一塊兒來,統一放在事件處理這一套框架下處理。數組

 libev的基本使用方法以下:數據結構

1框架

2less

3函數

4oop

5this

6繼承

7索引

8事件

9

10

11

12

13

14

15

16

17

18

19

20

21

int main (void)

{

// use the default event loop unless you have special needs

struct ev_loop *loop = EV_DEFAULT;

// initialise an io watcher, then start it

// this one will watch for stdin to become readable

ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/0, EV_READ);// 設置對stdin_watcher這個fd關注讀事件,並指定回調函數

ev_io_start (loop, &stdin_watcher);// 激活stdin_watcher這個fd,將其設置到loop中

// initialise a timer watcher, then start it

// simple non-repeating 5.5 second timeout

ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.);//設置一個定時器,並指定一個回調函數,這個timer只執行一次,5.5s後執行

ev_timer_start (loop, &timeout_watcher);//激活這個定時器,將其設置到loop中

// now wait for events to arrive

ev_run (loop, 0);//循環開始

// break was called, so exit

return 0;

}

libev中有一個抽象概念,叫作watcher(ev_watcher),libev中有各類各樣的watcher,好比定時器watcher(struct ev_timer),I/O watcher(struct ev_io) , 信號watcher(struct ev_signal)

等等。這三個具體的watcher至關於父類watcher的子類。這種繼承關係在C++這種高級語言中已內置,在C中實現就須要一些技巧,libev的做者使用了宏。

"父類"ev_watcher定義以下:

typedef structev_watcher

{

"color: #ff0000;">EV_WATCHER(ev_watcher)

} ev_watcher;

宏EV_WATCHER定義以下:

/* shared by all watchers */

#define EV_WATCHER(type)      \                                                                                    

int active;/* private */\    //該watcher是否被激活,加入到loop中

int pending;/* private */\  //該watcher關注的events是否已觸發

EV_DECL_PRIORITY/* private */\//int priority; 優先級,watcher是有優先級的

EV_COMMON/* rw */\        // void *data;  

EV_CB_DECLARE (type) /* private */// void (*cb)(struct ev_loop *loop, type *w, int revents);回調函數

再看一個"父類"ev_watcher_list的定義:

typedefstructev_watcher_list

{

EV_WATCHER_LIST (ev_watcher_list)

} ev_watcher_list;

宏EV_WATCHER_LIST定義以下:

#define EV_WATCHER_LIST(type)     \                                                                                

"color: #ff0000;"> EV_WATCHER (type)       \

structev_watcher_list *next; /* private */

能夠看出,ev_watcher_list 其實也是ev_watcher的一個"子類", 它多了一個成員變量 struct  ev_watcher_list *next;

這個成員變量用於將watcher串起來。

 如今看一個I/O watcher 這個最重要的"子類":

typedef struct ev_io

{

EV_WATCHER_LIST (ev_io)

int fd;    /* ro */// 顯而易見,與io相關聯的fd

int events;/* ro */// 這個watcher在fd上關注的事件

} ev_io;

能夠看出,ev_io是一種具體的watcher,它有兩個本身專有的成員變量fd和events

下面看一下最關鍵的一個數據結構:

struct ev_loop

{

ev_tstamp ev_rt_now;

#define ev_rt_now ((loop)->ev_rt_now)

// 這裏decl是declare的意思,ev_vars.h 裏面會定義一堆的變量,這些變量

// 都是本結構的成員,ev_vars.h展開的時候會用到下面這一行VAR的宏

#define VAR(name,decl) decl;

#include "ev_vars.h"

#undef VAR

};

ev_vars.h中包含不少關鍵的成員,好比:

epoll相關的成員變量:

#if EV_USE_EPOLL || EV_GENWRAP

VARx(struct epoll_event *, epoll_events)  // 至關於struct epoll_event *epoll_events                                 

VARx(int, epoll_eventmax) //目前epoll_events數組的大小,能夠擴充,每次以2倍的大小擴充

VARx(int, backend_fd) // 對於epoll來講,就是epoll使用的fd

//對於epoll來講,實際的函數是ev_epoll.c中的epoll_modify函數,這個函數會執行epoll_ctl

VAR (backend_modify, void(*backend_modify)(EV_P_intfd,intoev,intnev))

//對於epoll來講,實際的函數是ev_poll.c中的epoll_poll函數,這個函數會執行epoll_wait

VAR (backend_poll  , void(*backend_poll)(EV_P_ ev_tstamp timeout))

與fd相關的成員變量:

VARx(ANFD *, anfds)//這個數組是以fd爲索引

VARx(int, anfdmax) //上面數組的大小

VARx(int*, fdchanges) // fdchangemax大小的數組,每一個元素是一個fd,這個數組中存了全部epoll須要poll的fd

VARx(int, fdchangemax) //數組的容量

VARx(int, fdchangecnt) // 數組中實際的元素的大小

ANFD和fd一一對應,結構體ANFD以下:

typedefstruct

{

WL head; // typedef ev_watcher_list *WL; 關注同一個fd的事件的watcher的鏈表,一個fd能夠有多個watcher監聽它的事件

unsigned char events;/* the events watched for */// watcher鏈表中全部watcher關注的事件的按位與

unsigned char reify; /* flag set when this ANFD needs reification (EV_ANFD_REIFY, EV__IOFDSET) *///當這個結構體須要從新epoll_ctl則設置,說明關注的事件發生了變化

unsignedcharemask; /* the epoll backend stores the actual kernel mask in here *///實際發生的事件

unsignedcharunused;

#if EV_USE_EPOLL

unsigned int egen;   /* generation counter to counter epoll bugs */

#endif

#if EV_SELECT_IS_WINSOCKET || EV_USE_IOCP

SOCKET handle;

#endif

#if EV_USE_IOCP

OVERLAPPED or, ow;

#endif

} ANFD;

維護全部的"所關注的事件發生了的watcher",這些watcher的callback最後都須要被調用

VAR (pendings, ANPENDING *pendings [NUMPRI])  //watcher是有優先級的,libev爲每一個優先級的watcher維護一個數組

VAR (pendingmax, intpendingmax [NUMPRI]) // 每一個優先級watcher數組的容量

VAR (pendingcnt, intpendingcnt [NUMPRI]) //  每一個優先級watcher數組實際大小

struct ANPENDING以下:

typedefstruct

{

W w; //typedef ev_watcher *W;

int events;/* the pending event set for the given watcher *///events只指這個watcher關注了的而且已經發生了的尚未處理的事件

} ANPENDING;

從示例程序能夠看出,使用libev主要有幾個方法:

ev_io_init

ev_io_start

ev_timer_init

ev_timer_start

ev_run

一個個看:

ev_io_init是個宏,主要就是設置ev_io中的各個成員

void ev_io_start(struct ev_loop *loop, ev_io *w) 會作以下幾件事情:

1.  將參數w表示的watcher激活(w->active=1)

2.  將watcher w 加入到 w所關注的fd在anfds[](loop中)中相應的位置處的結構體ANFD中的watcher list鏈表中。

3.  將w所關注的fd加入到int *fdchanges數組中。

void ev_run(struct ev_loop *loop, int flags)最重要的函數,作以下幾件事:

1. 調用fd_reify。

    遍歷fdchanges數組:針對其中的每一個fd,作以下事情:

    將關注這個fd的全部的watcher都從ANFD的鏈表中取出來(經過fd去anfds[]中找到相應的ANFD結構體),而後將全部這些watcher關注的events經過epoll_ctl 給設置到

    kernel中。而後清空fdchanges數組,其實就是將fdchangecnt置爲0。

2. time_update 更新時間,校準時間

3. 計算給epoll_wait()使用的timeout,從維護全部的timer的最小堆中取堆頂(timers [HEAP0]),而後減去當前時間獲得timeout。最小堆使用一個數組實現的,每一個元素是

struct ANHE。

    timers定義以下:

VARx(ANHE *, timers)

  struct ANHE定義以下:

/* a heap element */

typedef struct{

ev_tstamp at;//timer watcher 到期時間

WT w;// typedef ev_watcher_time *WT;

} ANHE;

typedef struct ev_watcher_time

{

EV_WATCHER_TIME (ev_watcher_time)

} ev_watcher_time;

#define EV_WATCHER_TIME(type) \

EV_WATCHER (type) \

ev_tstamp at; /* private */

4. 調用backend_poll(loop, waittime)會作以下幾件事:

    對於使用epoll的系統來講,它其實是調用ev_epoll.c 中的epoll_poll()函數,這個函數主要流程以下:

    4.1. 調用epoll_wait()

    4.2. 遍歷每一個返回的 struct epoll_event,取出fd,和epoll_event中激活的事件,調用fd_event (loop, fd, got)函數。

            這個函數會更新loop的pending數組,將那些已經關注了一些事件的watcher,而且確實發生了的這些watcher給設置到loop的pending數組中

            注意這裏,got是全部watcher關注的事件的總和。ANPENDING結構體中的events僅僅單個watcher關注的events。

5. time_update 再次更新校準時間

6. timers_reify(loop) 若是最近的定時器已經觸發過了,則從新選出下一個最近的定時器,將其置於堆頂。

7.periodics_reify(loop) 和timers處理差很少

8. idle_reify(loop) 沒關注

9. EV_INVOKE_PENDING 從loop的pending數組中依次調用各個watcher的回調函數,優先級從高到低

10.若是當前loop中還有被激活的watcher,而且loop_done ==0 而且啓動ev_run(flags)的參數沒有設置EVRUN_ONCE和EVRUN_NOWAIT,則繼續從1開始。

到目前爲止,已經將timer和I/O事件進行統一了,如今看信號是如何統一到事件處理框架中的。

看看signal watcher的定義:

/* invoked when the given signal has been received */

/* revent EV_SIGNAL */

typedefstructev_signal

{

EV_WATCHER_LIST (ev_signal)

intsignum;/* ro *///信號id

} ev_signal;

ANSIG signals [EV_NSIG - 1]; // 每一個信號的信息用一個ANSIG結構體表示

typedefstruct

{

EV_ATOMIC_T pending;

#if EV_MULTIPLICITY

EV_P;

#endif

WL head;//能夠有多個watcher關注同一個信號,使用鏈表串起來

}ANSIG;

loop中和信號相關的主要成員以下:

VAR (evpipe, intevpipe [2])// 用於將信號處理和事件處理框架結合在一塊兒,evpipe[0]用於讀,evpipe[1]用於寫

VARx(ev_io, pipe_w)  //這個I/O ev就是用於封裝上面的pipe的讀端,讓epoll監聽這個pipe_w,當接收到信號的時候,只須要在信號處理函數中往evpipe[1]中寫便可

和I/O事件,timer事件相似,有以下兩個過程:

ev_signal_init : 宏,初始化這個ev_signal結構體

void ev_signal_start(struct ev_loop *loop, ev_signal *w)作以下幾件事:

1. 激活ev_signal這個watcher(w->active = 1)

2. 將w串到signals數組中相應位置的watcher list中

3. 初始化一對pipe,這對pipe就是用於將信號和事件處理框架結合起來,而且註冊一個ev_io pipe_w到epoll中,pipe_w封裝的是int evpipe[2]的讀端evpipe[0]

4. 註冊這個信號的信號處理函數ev_sighandler,信號處理函數會將signals數組中相應的槽位的pending置1,意思是已接收到這個信號,而且往int evpipe[2]的寫端

evpipe[1]寫入一個字節,這樣在pipe_w這個IO event就有了讀事件,從而epoll_wait()返回,成功的將信號和事件處理框架結合起來。

至此,libev如何將IO 事件,timer事件和信號統一到事件處理框架下已分析完成。能夠看出,libev和libevent的作法是同樣的,只是實現不同,好比,關於繼承的實現,libev使用宏來實現至關的難讀,可是這樣作是高效的,借用霸爺的話說:"在libev的世界中,效率第一"。這可能正是大多數人說,libev比libevent代碼難讀的緣由

相關文章
相關標籤/搜索