前段時(shí)間對Libevent的源碼進(jìn)行了閱讀,現(xiàn)整理如下:

介紹

libevent是一個輕量級的開源高性能事件驅(qū)動網(wǎng)絡(luò)庫,是一個典型的Reactor模型。其主要特點(diǎn)有事件驅(qū)動,高性能,跨平臺,統(tǒng)一事件源等等。
網(wǎng)上關(guān)于libevent的源碼分析有很多相關(guān)博客,本人在學(xué)習(xí)過程中也是借助了網(wǎng)絡(luò)。所以,在此,關(guān)于libevent中的許多具體實(shí)現(xiàn)部分就不做介紹,主要是從相關(guān)數(shù)據(jù)結(jié)構(gòu)層面上去分析。僅供參考。

event結(jié)構(gòu)體

  • libevent中的事件處理類型是event結(jié)構(gòu)類型,event結(jié)構(gòu)體封裝了句柄,事件類型,回調(diào)函數(shù),以及其他必要的標(biāo)志和數(shù)據(jù),是整個libevent庫的核心。

  • 該結(jié)構(gòu)的定義如下:

    struct event{    /*     * ev_next, ev_active_next都是雙向鏈表節(jié)點(diǎn)指針     * 它們是libevent對不同事件類型和在不同時(shí)期,對事件的管理時(shí)使用到的字段     *     * libevent使用雙向鏈表保存所有注冊的IO和signal事件     * ev_next 就是該IO事件在鏈表中的位置,稱此鏈表為已注冊事件鏈表     * ev_active_next: libevent將所有激活事件放入鏈表active list中,然后遍歷active list     * 執(zhí)行調(diào)度,ev_active_next就指明了event在active list中的位置     */
        TAILQ_ENTRY(event) ev_next;
        TAILQ_ENTRY(event) ev_active_next;    /*     * _ev 是一個聯(lián)合體,所有具有相同描述符的IO事件通過ev.ev_io.ev_io_next成員串聯(lián)成一個     * 尾隊(duì)列,稱之為IO事件隊(duì)列,所有具有相同信號值的信號事件通過ev.ev_signal.ev_signal_next     * 串聯(lián)成一個尾隊(duì)列,稱之為信號事件隊(duì)列。ev.ev_signal.ev_ncalls成員指定時(shí)間發(fā)生時(shí),Reactor     * 需要執(zhí)行多少次該事件對應(yīng)的回調(diào)函數(shù),ev.ev_signal.ev_pcalls要么是NULL,要么執(zhí)行ev.ev_signal.ev_ncalls     */
        union{        struct {
                TAILQ_ENTRY(event) ev_io_next;            struct timeval ev_timeout;
            }ev_io;        struct {
                TAILQ_ENTRY(event) ev_signal_next;            short ev_ncalls;            short *ev_pcalls;
            }ev_signal;
        } _ev;    /*     * ev_timeout_pos是一個聯(lián)合體,它僅用于定時(shí)事件處理器,老版本libevnet中使用最小堆管理     * 定時(shí)器,但是開發(fā)者認(rèn)為有時(shí)簡單鏈表的管理更加高效。所以新版本中引入了“通用定時(shí)器”的     * 概念。這些定時(shí)器不是存儲在時(shí)間堆中,而是存儲在尾隊(duì)列中,我們稱之為通用定時(shí)器隊(duì)列。     * 對于通用定時(shí)器而言,ev_timeout_pos中的ev_next_with_common_timeout成員指出了該定時(shí)器     * 在隊(duì)列中的位置;對于其他定時(shí)器,min_heap_idx成員指出了該定時(shí)器在時(shí)間堆中的位置。一個     * 定時(shí)器是否是通用定時(shí)器,取決于其超時(shí)值的大小。具體參考event.c中的is_common_timeout函數(shù)。     */
        union{
            TAILQ_ENTRY(event) ev_next_with_common_timeout;        int min_heap_idx;
        }ev_timeout_pos;    //如果是超時(shí)事件ev_timeout超時(shí)值
        struct timeval ev_timeout;    //ev_base :該事件所屬的反應(yīng)堆實(shí)例,這是一個event_base結(jié)構(gòu)體
        struct event_base *ev_base;    //對于IO事件,是綁定的文件描述符,對于signal事件,是綁定的信號
        int ev_fd;    /*     * ev_events : event關(guān)注的事件類型,它可以是以下三種類型:     * IO事件:EV_WRITE /  EV_READ     * 定時(shí)事件: EV_TIMEOUT       * 信號: EV_SIGANL     *輔助選項(xiàng): EV_PERSIST, 表明是一個永久事件     */
        short ev_events;  //各個事件可以使用 "|"運(yùn)算符進(jìn)行組合,信號和IO事件不能同時(shí)設(shè)置
    
        //事件就緒執(zhí)行時(shí),調(diào)用ev_callback的次數(shù),通常為1
        short ev_ncalls;    //指針,指向ev_ncalls或NULL
        short *ev_pncalls;  //allows deletes in callback
    
        int ev_pri;  //smaller numbers are higher priority
    
        //ev_callback:event回調(diào)函數(shù),被ev_base調(diào)用,執(zhí)行事件處理程序,這是一個函數(shù)指針
        //其中fd對應(yīng)ev_fd, events對應(yīng)ev_events, arg對應(yīng)ev_arg
        void (*ev_callback)(int , short, void *arg);    //void* 表明可以是任意類型,在設(shè)置event時(shí)指定
        void *ev_arg;    //記錄了當(dāng)前激活事件的類型
        int ev_res;   //result passed to event callback
    
        /*     * libevent用于標(biāo)記event信息的字段,表明當(dāng)前的狀態(tài)    */
        int ev_flags;
    };

    從event結(jié)構(gòu)的定義可以看出,event中封裝了句柄,回調(diào)函數(shù),和事件類型。包括該事件在相應(yīng)鏈表或時(shí)間堆中的索引位置。宏TAILQ_ENTRY是尾隊(duì)列的節(jié)點(diǎn)類型,其定義為:

    #define TAILQ_ENTRY(type)       \    struct {                 \        struct type *tqe_next;  \  /*下一個元素*/   
            struct type **tqe_prev; \ /*前一個元素的地址*/  
        }
  • 每當(dāng)有事件event轉(zhuǎn)變?yōu)榫途w狀態(tài)時(shí),libevent就會把它移入到active event list[priority]中,其中priority是event的優(yōu)先級;接著libevent會根據(jù)自己的調(diào)度策略選擇就緒事件,調(diào)用其cb_callback()函數(shù)執(zhí)行事件處理。

事件處理框架 event_base

  • 結(jié)構(gòu)體event_base是libevent的Reactor,其聲明如下:

    struct event_base {    /* 初始化Reactor的時(shí)候選擇一種后端IO復(fù)用機(jī)制,并記錄在如下字段中*/
        const struct eventop *evsel;    /*指向IO復(fù)用機(jī)制真正存儲的數(shù)據(jù),它通過evsel成員的init函數(shù)來初始化*/
        void *evbase;    /* 指向信號的后端處理機(jī)制,目前僅在signal.h文件中定義了一種處理方法*/
        const struct eventop *evsigsel;    void *evsigbase;    /*信號處理器使用到的數(shù)據(jù)結(jié)構(gòu),其中封裝了一個socketpair創(chuàng)建的管道,它用于信號處理函數(shù)和事件多路分發(fā)器之間的通信。*/
        struct evsig_info sig;    /* 添加到該event_base的所有事件和激活事件的數(shù)量*/
        int event_count;        /**< counts number of total events */
        int event_count_active;     /**< counts number of active events */
    
        /* 是否執(zhí)行完活動事件隊(duì)列上的剩余任務(wù)之后就退出事件循環(huán) */
        int event_gotterm;      /**< Set to terminate loop once done
                     * processing events. */
        /* 是否立即退出事件循環(huán),而不管是否還有任務(wù)需要處理 */
        int event_break;        /**< Set to exit loop immediately */
    
        /* 活動事件隊(duì)列數(shù)組。索引越小的隊(duì)列,優(yōu)先級越高。高優(yōu)先級的活動事件隊(duì)列中的事件處理器將被優(yōu)先處理*/
        struct event_list **activequeues;    /* 活動事件隊(duì)列數(shù)組的大小,即該event_base一共有nactivequeues個不同優(yōu)先級的活動事件隊(duì)列*/
        int nactivequeues;    /*是否應(yīng)該啟動一個新的事件循環(huán)*/
        int event_continue;    //目前正在處理的活動事件隊(duì)列的優(yōu)先級
        int event_running_priority;    //事件循環(huán)是否已經(jīng)啟動
        int running_loop;    /** Deferred callback management: a list of deferred callbacks to
        * run active the active events. */
        TAILQ_HEAD (deferred_cb_list, deferred_cb) deferred_cb_list;    //文件描述符和IO事件之間的映射關(guān)系表
        struct event_io_map io;    /*信號值和信號事件之間的映射關(guān)系表*/
        struct event_signal_map sigmap;    /*注冊時(shí)間隊(duì)列,存放IO事件處理器和信號事件處理器*/
        struct event_list eventqueue;    /*時(shí)間堆*/
        struct min_heap timeheap;    //系統(tǒng)管理時(shí)間的一些成員
        struct timeval event_tv;    struct timeval tv_cache;
    };

    其中:

    • evsel和evbase這兩個字段的額設(shè)置可能會讓人迷惑,可以將其看作是類和靜態(tài)函數(shù)的關(guān)系,比如添加事件時(shí)的調(diào)用行為:evsel->add(evbase, ev),實(shí)際上執(zhí)行操作的是evbase,這相當(dāng)于class::add(instance, ev),instance就是class的一個實(shí)例。evsel指向全局變量static const struct eventop *eventops[]中的一個。eventops[]包含了select,poll,kequeue和epoll等等其中的若干個全局實(shí)例對象。evbase實(shí)際上是一個eventop實(shí)例對象。

    • eventop結(jié)構(gòu)體,是一系列的函數(shù)指針,定義如下:

      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 *); //事件分發(fā)
          void (*dealloc)(struct event_base*, void *);  //注銷,釋放資源
          //set if we need to reinitialize the event_base
          int need_reinit;
      };

      在libevent中,每個IO事件分發(fā)機(jī)制的實(shí)現(xiàn)都必須提供這五個函數(shù)接口。

  • 事件主循環(huán)主要是通過event_base_loop()函數(shù)來完成的。其代碼如下:

    int event_base_loop(struct event_base *base, int flags){    const struct eventop *evsel = base->evsel;    void *evbase = base->evbase;    struct timeval tv;    struct timeval *tv_p;    int res, done;    //清空時(shí)間緩存
        base->tv_cache.tv_sec = 0;    //evsignal_base是全局變量,在處理signal時(shí),用于指明signal所屬的event_base實(shí)例
        if(base->sig.ev_signal_added)
            evsignal_base = base;
    
        done = 0;    //事件主循環(huán)
        while(!done){        //查看是否需要跳出循環(huán),程序可以調(diào)用event_loopexit_cb()設(shè)置event_gotterm標(biāo)記
            //調(diào)用event_base_loopbreak設(shè)置event_break標(biāo)志
            if(base->event_gotterm){
                base->event_gotterm = 0;            break;
            }        if(base->event_break){
                base->event_break = 0;            break;
            }        //you cannot use this interface for multi-threaded apps
            while(event_gotsig){
                event_gotsig = 0;            if(event_sigcb){
                    res = (*event_sigcb)();                if(res == -1){
                        errno = EINTR;                    return -1;
                    }
                }
            }        //校正系統(tǒng)時(shí)間,如果系統(tǒng)使用的是非MONOTONIC時(shí)間,用戶可能會向后調(diào)整了系統(tǒng)時(shí)間
            //在timeout_correct函數(shù)中,比較last wait time和當(dāng)前事件,如果
            //當(dāng)前時(shí)間 < last wait time
            //表明時(shí)間有問題,這需要更新timer_heap中所有定時(shí)事件的超時(shí)時(shí)間
            timeout_correct(base, &tv);        //根據(jù)time heap中事件的最小超時(shí)時(shí)間,計(jì)算系統(tǒng)IO demultiplexer的最大等待時(shí)間
            tp_p = &tv;        if(!base->event_count_active && !(flags & EVLOOP_NONBLOCK)){
                timeout_next(base, &tv_p);
            }        else{            //依然有未處理的就緒時(shí)間,就讓IO demultiplexer立即返回,不必等待
                //下面會提到,在libevent中,低優(yōu)先級的就緒事件可能不能立即被處理
                evutil_timerclear(&tv);
            }        //如果當(dāng)前沒有注冊事件,就退出
            if(!event_haveevents(base)){
                event_debug("%s: no events registered.", __func__);            return 1;
            }        //更新last wait time,并清空time cache
            gettime(base, &base->event_tv);
            base->tv_cache.tv_sec = 0;        //調(diào)用系統(tǒng)IO demultiplexer等待就緒IO events,可能是epoll_wait,或者select等
            //在evsel->dispatch()中,會把就緒signal event /  IO event插入到激活鏈表中
            res = evsel->dispatch(base, evbase, tv_p);        if(res == -1)            return -1;        //將time cache 賦值為當(dāng)前系統(tǒng)時(shí)間
            gettime(base, &base->tv_cache);        //檢查heap中的timer events,將就緒的timer event從heap上刪除,并插入到激活鏈表中
            timeout_process(base);        //調(diào)用event_process_active()處理激活鏈表中的就緒event,調(diào)用其回調(diào)函數(shù)執(zhí)行事件處理
            //該函數(shù)會尋找最高優(yōu)先級(priority值越小優(yōu)先級越高)的激活事件鏈表
            //然后處理鏈表中的所有就緒事件
            //因此低優(yōu)先級的就緒事件可能得不得及時(shí)處理
            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;
            }    //循環(huán)結(jié)束,清空時(shí)間緩存
        base->tv_cache.tv_sec = 0;
    
        event_debug("%s: asked to terminate loop.", __func__);    return 0;
    }
  • 統(tǒng)一事件源,libevent將timer和signal事件都統(tǒng)一到了系統(tǒng)的IO demultiplex機(jī)制中

    • 通過socketpair來實(shí)現(xiàn)的。即一個socket對,其中有兩個socket,一個讀,一個寫。

    • 將讀socket在事件主循環(huán)實(shí)例中注冊一個讀事件,當(dāng)信號發(fā)生時(shí),往寫socket中寫入一個字符,通常為信號值,此時(shí)讀socket上有讀事件,觸發(fā)IO demultiplex讀事件,然后同普通的IO事件一起被處理即可。

    • timer和IO事件的統(tǒng)一。因?yàn)橄到y(tǒng)的IO機(jī)制,例如select()和epoll_wait()都允許程序制定一個最大的等待時(shí)間,根據(jù)所有timer事件中的最小超時(shí)時(shí)間來設(shè)置IO demultiplex的最大等待時(shí)間,當(dāng)IO返回時(shí),再激活所有就緒的timer事件就可以了,這樣就將timer事件完美融合到了系統(tǒng)的IO機(jī)制中了。

    • IO和signal的統(tǒng)一。因?yàn)閟ignal的出現(xiàn)對進(jìn)程來說是完全隨機(jī)的。所以當(dāng)signal發(fā)生時(shí),并不立即調(diào)用event的callback函數(shù)處理信號,而是設(shè)法通知系統(tǒng)的IO機(jī)制,讓其返回,然后再統(tǒng)一和IO事件,以及timer一起處理。

  • 事件主循環(huán)的流程如下

    1) 開始
    2) 調(diào)整系統(tǒng)時(shí)間與否
    3) 根據(jù)timer heap中的event的最小超時(shí)時(shí)間計(jì)算系統(tǒng)IO demultiplexer的最大等待時(shí)間
    4) 更新last wait time, 并清空time cache5) 調(diào)用系統(tǒng)I/O demultiplexer等待就緒I/O events6) 檢查signal的激活標(biāo)記,如果被設(shè)置,則檢查激活signal event,并將event插入到激活鏈表中7) 將就緒的I/O event插入到激活鏈表中8) 檢查heap中的timer events,將就緒的timer event從heap上刪除,并插入到激活鏈表中9) 根據(jù)優(yōu)先級處理激活鏈表中的就緒event,調(diào)用其回調(diào)函數(shù)執(zhí)行事件處理(優(yōu)先級越小越高)10) 結(jié)束

總結(jié)

  • 本文只是從libevent中的事件循環(huán)主框架上面做了一個簡單的介紹。

  • 若是想要了解libenvet更多的具體實(shí)現(xiàn)細(xì)節(jié),請參考網(wǎng)絡(luò)

  • 本文參考了libevent源碼深度剖析 和《linux高性能服務(wù)器編程》

標(biāo)簽: libevent

http://www.cnblogs.com/lengender-12/p/6747258.html