libevent,zeromq,和muduo三個(gè)網(wǎng)絡(luò)庫對(duì)比分析

本文將libevent,zeromq,和muduo三個(gè)網(wǎng)絡(luò)庫進(jìn)行對(duì)比分析:

libevent:

    1. 數(shù)組定義TAILQ_HEAD和TAILQ_ENTRY:

    #define TAILQ_HEAD(name, type)                        \

    struct name {                                \

        struct type *tqh_first;    /* first element */            \

        struct type **tqh_last;    /* 二級(jí)指針指向最后一個(gè)type的tqe_prev變量 */        \

    }
    //和前面的TAILQ_HEAD不同活鹰,這里的結(jié)構(gòu)體并沒有name.即沒有結(jié)構(gòu)體名陈哑。

    //所以該結(jié)構(gòu)體只能作為一個(gè)匿名結(jié)構(gòu)體悠瞬。所以叽躯,它一般都是另外一個(gè)結(jié)構(gòu)體

    //或者共用體的成員

    #define TAILQ_ENTRY(type)                        \

    struct {                                \

        struct type *tqe_next;    /* next element */            \

        struct type **tqe_prev;    /* address of previous next element */    \

    }   

  • 2.信號(hào)event的處理
    采用統(tǒng)一事件源的方式來處理信號(hào)event,即將信號(hào)轉(zhuǎn)換成IO來處理。
    統(tǒng)一事件源的工作原理如下:假如用戶要監(jiān)聽SIGINT信號(hào)诀紊,那么在實(shí)現(xiàn)的內(nèi)部就對(duì)SIGINT這個(gè)信號(hào)設(shè)置捕抓函數(shù)述暂。此外娩鹉,在實(shí)現(xiàn)的內(nèi)部還要建立一條管道(pipe),并把這個(gè)管道加入到多路IO復(fù)用函數(shù)中元媚。當(dāng)SIGINT這個(gè)信號(hào)發(fā)生后轧叽,捕抓函數(shù)將會(huì)被調(diào)用。而這個(gè)捕抓函數(shù)的工作就是往管道寫入一個(gè)字符(這個(gè)字符往往等于所捕抓到信號(hào)的信號(hào)值)惠毁。此時(shí)犹芹,這個(gè)管道就變成是可讀的了,多路IO復(fù)用函數(shù)能檢測(cè)到這個(gè)管道變成可讀的了鞠绰。換言之腰埂,多路IO復(fù)用函數(shù)檢測(cè)到SIGINT信號(hào)的發(fā)生,也就完成了對(duì)信號(hào)的監(jiān)聽工作蜈膨。
    具體實(shí)現(xiàn)細(xì)節(jié):

    1.創(chuàng)建一個(gè)管道(實(shí)際上使用socketpair);

    2.為這個(gè)socketpair的一個(gè)讀端創(chuàng)建一個(gè)event,并將之加入到多路IO復(fù)用函數(shù)的監(jiān)聽中屿笼;

    說明:以上2條都是在選定一個(gè)多路IO復(fù)用函數(shù)后牺荠,就會(huì)調(diào)用:

           base->evbase = base->evsel->init(base);
    

    選定的IO復(fù)用Init函數(shù)會(huì)創(chuàng)建一個(gè)socketpair,并將其讀端與ev_signal這個(gè)event相關(guān)聯(lián),用于監(jiān)聽信號(hào)驴一;

    3.設(shè)置信號(hào)抓捕函數(shù);

    4.有信號(hào)發(fā)生休雌,往socketpair寫入一個(gè)字節(jié);

    說明:函數(shù)event_add會(huì)將信號(hào)event加入到event_base中,其中會(huì)調(diào)用到信號(hào)event的add函數(shù)evsig_add,此函數(shù)中會(huì)設(shè)置libevent的信號(hào)抓捕函數(shù),并將ev_signal作為信號(hào)監(jiān)聽的時(shí)間來通知event_base有信號(hào)發(fā)生了肝断。只需一個(gè)event即可完成工作杈曲,即使用戶要監(jiān)聽多個(gè)不同的信號(hào),因?yàn)檫@個(gè)event已經(jīng)和socketpair的讀端相關(guān)聯(lián)了胸懈。如果要監(jiān)聽多個(gè)信號(hào)担扑,那么就在信號(hào)處理函數(shù)中往這個(gè)socketpair寫入不同的值即可。event_base能監(jiān)聽到可讀趣钱,并可以從讀到的內(nèi)容可以判斷是哪個(gè)信號(hào)發(fā)生了涌献。

    注意:當(dāng)我們對(duì)某個(gè)信號(hào)進(jìn)行event_new和event_add后,就不應(yīng)該再次設(shè)置該信號(hào)的信號(hào)捕抓函數(shù)首有。否則event_base將無法監(jiān)聽到信號(hào)的發(fā)生燕垃。

  • 3.bufferevent:用于管理和調(diào)度IO事件,托管客戶端連接上的讀事件井联,寫時(shí)間卜壕,和事件處理。

    主要函數(shù)包括:

    //創(chuàng)建一個(gè)Bufferevent  
    struct bufferevent *bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, enum bufferevent_options options); 
    
    //釋放Bufferevent
    void bufferevent_free(struct bufferevent *bev); 
    
    //設(shè)置回調(diào)函數(shù)
    void bufferevent_setcb(struct bufferevent *bufev,bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void *cbarg);
    
    //設(shè)置buffer時(shí)間類型
    bufferevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST);
    
    //設(shè)置水位
    void bufferevent_setwatermark(struct bufferevent *bufev, short events,size_t lowmark, size_t highmark);
    
    //寫入  
    int bufferevent_write(struct bufferevent *bufev, const void *data, size_t size);
    
    //輸出  
    size_t bufferevent_read(struct bufferevent *bufev, void *data, size_t size);
    

    水位設(shè)置說明:
    低水位:表示當(dāng)evbuffer緩沖區(qū)的數(shù)據(jù)少于該水位時(shí)低矮,不會(huì)觸發(fā)用戶設(shè)定的讀回調(diào)函數(shù)印叁;
    高水位:表示當(dāng)evbuffer緩沖區(qū)的數(shù)據(jù)大于該水位時(shí),將不會(huì)從socket的緩沖區(qū)繼續(xù)讀取數(shù)據(jù)军掂,避免了因socket緩沖區(qū)有數(shù)據(jù)而一直觸發(fā)監(jiān)聽讀的event形成的死循環(huán)轮蜕。當(dāng)水位低于該水位時(shí)將繼續(xù)從socket緩沖區(qū)讀取數(shù)據(jù);

    讀監(jiān)聽和寫監(jiān)聽的區(qū)別:
    讀監(jiān)聽:當(dāng)監(jiān)聽讀事件的event檢測(cè)到socket讀緩沖區(qū)有數(shù)據(jù)時(shí)即認(rèn)為可讀蝗锥,觸發(fā)讀回調(diào)函數(shù)跃洛;
    寫監(jiān)聽:當(dāng)監(jiān)聽寫事件的event檢測(cè)到socket寫緩沖區(qū)可寫時(shí)認(rèn)為可寫,調(diào)用寫回調(diào)函數(shù),因?yàn)閟ocket寫緩沖區(qū)大部分時(shí)間都是空的终议,所以這樣判斷會(huì)導(dǎo)致event一直觸發(fā)寫回調(diào)函數(shù)汇竭,造成死循環(huán)。實(shí)際libevent只用在調(diào)用寫入函數(shù)bufferevent_write時(shí)才會(huì)將監(jiān)聽寫的事件event添加(event_add)到event_base中進(jìn)行監(jiān)聽穴张,當(dāng)所有數(shù)據(jù)都寫入到socket緩沖區(qū)后就從event_base刪除event,避免形成死循環(huán)细燎;

  • 4.evbuffer:

     //evbuffer-internal.h文件
    
     struct evbuffer_chain;
     struct evbuffer {
    
            struct evbuffer_chain *first;
    
            struct evbuffer_chain *last;
    
            //這是一個(gè)二級(jí)指針。使用*last_with_datap時(shí)皂甘,指向的是鏈表中最后一個(gè)有數(shù)據(jù)的evbuffer_chain玻驻。
            //所以last_with_datap存儲(chǔ)的是倒數(shù)第二個(gè)evbuffer_chain的next成員地址。
            //一開始buffer->last_with_datap = &buffer->first;此時(shí)first為NULL。所以當(dāng)鏈表沒有節(jié)點(diǎn)時(shí)
            //*last_with_datap為NULL璧瞬。當(dāng)只有一個(gè)節(jié)點(diǎn)時(shí)*last_with_datap就是first户辫。    
            struct evbuffer_chain **last_with_data;
    
            size_t total_len;//鏈表中所有chain的總字節(jié)數(shù)
            ...
     };
    
     struct evbuffer_chain {
    
            struct evbuffer_chain *next;
    
            size_t buffer_len;//buffer的大小
    
            //錯(cuò)開不使用的空間。該成員的值一般等于0
            ev_off_t misalign;
    
            //evbuffer_chain已存數(shù)據(jù)的字節(jié)數(shù)
            //所以要從buffer + misalign + off的位置開始寫入數(shù)據(jù)
            size_t off;
            ...
            unsigned char *buffer;
      };
    

說明:evbuffer為鏈表結(jié)構(gòu)嗤锉,鏈表的每個(gè)節(jié)點(diǎn)為evbuffer_chain渔欢,并且用二級(jí)指針last_with_data指向鏈表中最后一個(gè)有數(shù)據(jù)的成員地址。
evbuffer結(jié)構(gòu)圖解析:

evbuffer.png

如圖:在evbuffer鏈表中存在沒有數(shù)據(jù)的空節(jié)點(diǎn)瘟忱,這個(gè)如同STL中的vector一樣奥额,提前分配好空閑內(nèi)存,在下次添加數(shù)據(jù)時(shí),不需要額外申請(qǐng)空間就能保存數(shù)據(jù)访诱。

注意:evbuffer_chain中buffer指針指向的地址中保存的是真正的數(shù)據(jù)披坏,按理來說應(yīng)該用malloc單獨(dú)分配一塊內(nèi)存,但此處buffer指向的內(nèi)存是和evbuffer_chain本身存儲(chǔ)的內(nèi)存是一塊分配的盐数,代碼是在函數(shù)evbuffer_chain_new()中。

鏈表尾添加數(shù)據(jù):

int evbuffer_add(struct evbuffer *buf, const void *data_in, size_t datlen)

1.當(dāng)鏈表為空時(shí)直接新建一個(gè)evbuffer_chain伞梯,并調(diào)用函數(shù)evbuffer_chain_insert()添加到鏈表中玫氢。
2.當(dāng)鏈表最后一個(gè)有數(shù)據(jù)的chain(即last_with_data指向的內(nèi)存地址) 的空閑內(nèi)存大于用戶數(shù)據(jù)size時(shí),直接添加到此chain谜诫。
3.當(dāng)鏈表最后一個(gè)有數(shù)據(jù)的chain(即last_with_data指向的內(nèi)存地址) 的空閑內(nèi)存小于于用戶數(shù)據(jù)size時(shí)漾峡,這時(shí)就把數(shù)據(jù)分開到兩個(gè)chain中保存。

鏈表頭添加數(shù)據(jù):

int evbuffer_prepend(struct evbuffer *buf, const void *data, size_t datlen)

說明: misalign表示空閑未用的空間喻旷,可以隨時(shí)使用生逸。在頭部添加數(shù)據(jù)時(shí),新加數(shù)據(jù)使用的空間其實(shí)就是chain中的misalign空間且预,新建的chain的misalign空間就是全部的buffer內(nèi)存槽袄,隨著數(shù)據(jù)添加,misalign會(huì)越來越小锋谐。

Libevent學(xué)習(xí)參考鏈接

待續(xù)...

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末遍尺,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涮拗,更是在濱河造成了極大的恐慌乾戏,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件三热,死亡現(xiàn)場(chǎng)離奇詭異鼓择,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)就漾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門呐能,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人从藤,你說我怎么就攤上這事催跪∷洌” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵懊蒸,是天一觀的道長荣倾。 經(jīng)常有香客問我,道長骑丸,這世上最難降的妖魔是什么舌仍? 我笑而不...
    開封第一講書人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮通危,結(jié)果婚禮上铸豁,老公的妹妹穿的比我還像新娘。我一直安慰自己菊碟,他們只是感情好节芥,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著逆害,像睡著了一般头镊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上魄幕,一...
    開封第一講書人閱讀 49,111評(píng)論 1 285
  • 那天相艇,我揣著相機(jī)與錄音,去河邊找鬼纯陨。 笑死坛芽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的翼抠。 我是一名探鬼主播咙轩,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼阴颖!你這毒婦竟也來了臭墨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤膘盖,失蹤者是張志新(化名)和其女友劉穎胧弛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體侠畔,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡结缚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了软棺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片红竭。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出茵宪,到底是詐尸還是另有隱情最冰,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布稀火,位于F島的核電站暖哨,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏凰狞。R本人自食惡果不足惜篇裁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赡若。 院中可真熱鬧达布,春花似錦、人聲如沸逾冬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽身腻。三九已至分冈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間霸株,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來泰國打工集乔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留去件,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓扰路,卻偏偏與公主長得像尤溜,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子汗唱,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345