libevent,zeromq,和muduo三个网络库对比分析

本文将libevent,zeromq,和muduo三个网络库进行对比分析:

libevent:

    1. 数组定义TAILQ_HEAD和TAILQ_ENTRY:

    #define TAILQ_HEAD(name, type)                        \

    struct name {                                \

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

        struct type **tqh_last;    /* 二级指针指向最后一个type的tqe_prev变量 */        \

    }
    //和前面的TAILQ_HEAD不同,这里的结构体并没有name.即没有结构体名。

    //所以该结构体只能作为一个匿名结构体。所以,它一般都是另外一个结构体

    //或者共用体的成员

    #define TAILQ_ENTRY(type)                        \

    struct {                                \

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

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

    }   

  • 2.信号event的处理
    采用统一事件源的方式来处理信号event,即将信号转换成IO来处理。
    统一事件源的工作原理如下:假如用户要监听SIGINT信号,那么在实现的内部就对SIGINT这个信号设置捕抓函数。此外,在实现的内部还要建立一条管道(pipe),并把这个管道加入到多路IO复用函数中。当SIGINT这个信号发生后,捕抓函数将会被调用。而这个捕抓函数的工作就是往管道写入一个字符(这个字符往往等于所捕抓到信号的信号值)。此时,这个管道就变成是可读的了,多路IO复用函数能检测到这个管道变成可读的了。换言之,多路IO复用函数检测到SIGINT信号的发生,也就完成了对信号的监听工作。
    具体实现细节:

    1.创建一个管道(实际上使用socketpair);

    2.为这个socketpair的一个读端创建一个event,并将之加入到多路IO复用函数的监听中;

    说明:以上2条都是在选定一个多路IO复用函数后,就会调用:

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

    选定的IO复用Init函数会创建一个socketpair,并将其读端与ev_signal这个event相关联,用于监听信号;

    3.设置信号抓捕函数;

    4.有信号发生,往socketpair写入一个字节;

    说明:函数event_add会将信号event加入到event_base中,其中会调用到信号event的add函数evsig_add,此函数中会设置libevent的信号抓捕函数,并将ev_signal作为信号监听的时间来通知event_base有信号发生了。只需一个event即可完成工作,即使用户要监听多个不同的信号,因为这个event已经和socketpair的读端相关联了。如果要监听多个信号,那么就在信号处理函数中往这个socketpair写入不同的值即可。event_base能监听到可读,并可以从读到的内容可以判断是哪个信号发生了。

    注意:当我们对某个信号进行event_new和event_add后,就不应该再次设置该信号的信号捕抓函数。否则event_base将无法监听到信号的发生。

  • 3.bufferevent:用于管理和调度IO事件,托管客户端连接上的读事件,写时间,和事件处理。

    主要函数包括:

    //创建一个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); 
    
    //设置回调函数
    void bufferevent_setcb(struct bufferevent *bufev,bufferevent_data_cb readcb,bufferevent_data_cb writecb,bufferevent_event_cb eventcb,void *cbarg);
    
    //设置buffer时间类型
    bufferevent_enable(bev, EV_READ|EV_WRITE|EV_PERSIST);
    
    //设置水位
    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);
    

    水位设置说明:
    低水位:表示当evbuffer缓冲区的数据少于该水位时,不会触发用户设定的读回调函数;
    高水位:表示当evbuffer缓冲区的数据大于该水位时,将不会从socket的缓冲区继续读取数据,避免了因socket缓冲区有数据而一直触发监听读的event形成的死循环。当水位低于该水位时将继续从socket缓冲区读取数据;

    读监听和写监听的区别:
    读监听:当监听读事件的event检测到socket读缓冲区有数据时即认为可读,触发读回调函数;
    写监听:当监听写事件的event检测到socket写缓冲区可写时认为可写,调用写回调函数,因为socket写缓冲区大部分时间都是空的,所以这样判断会导致event一直触发写回调函数,造成死循环。实际libevent只用在调用写入函数bufferevent_write时才会将监听写的事件event添加(event_add)到event_base中进行监听,当所有数据都写入到socket缓冲区后就从event_base删除event,避免形成死循环;

  • 4.evbuffer:

     //evbuffer-internal.h文件
    
     struct evbuffer_chain;
     struct evbuffer {
    
            struct evbuffer_chain *first;
    
            struct evbuffer_chain *last;
    
            //这是一个二级指针。使用*last_with_datap时,指向的是链表中最后一个有数据的evbuffer_chain。
            //所以last_with_datap存储的是倒数第二个evbuffer_chain的next成员地址。
            //一开始buffer->last_with_datap = &buffer->first;此时first为NULL。所以当链表没有节点时
            //*last_with_datap为NULL。当只有一个节点时*last_with_datap就是first。    
            struct evbuffer_chain **last_with_data;
    
            size_t total_len;//链表中所有chain的总字节数
            ...
     };
    
     struct evbuffer_chain {
    
            struct evbuffer_chain *next;
    
            size_t buffer_len;//buffer的大小
    
            //错开不使用的空间。该成员的值一般等于0
            ev_off_t misalign;
    
            //evbuffer_chain已存数据的字节数
            //所以要从buffer + misalign + off的位置开始写入数据
            size_t off;
            ...
            unsigned char *buffer;
      };
    

说明:evbuffer为链表结构,链表的每个节点为evbuffer_chain,并且用二级指针last_with_data指向链表中最后一个有数据的成员地址。
evbuffer结构图解析:

evbuffer.png

如图:在evbuffer链表中存在没有数据的空节点,这个如同STL中的vector一样,提前分配好空闲内存,在下次添加数据时,不需要额外申请空间就能保存数据。

注意:evbuffer_chain中buffer指针指向的地址中保存的是真正的数据,按理来说应该用malloc单独分配一块内存,但此处buffer指向的内存是和evbuffer_chain本身存储的内存是一块分配的,代码是在函数evbuffer_chain_new()中。

链表尾添加数据:

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

1.当链表为空时直接新建一个evbuffer_chain,并调用函数evbuffer_chain_insert()添加到链表中。
2.当链表最后一个有数据的chain(即last_with_data指向的内存地址) 的空闲内存大于用户数据size时,直接添加到此chain。
3.当链表最后一个有数据的chain(即last_with_data指向的内存地址) 的空闲内存小于于用户数据size时,这时就把数据分开到两个chain中保存。

链表头添加数据:

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

说明: misalign表示空闲未用的空间,可以随时使用。在头部添加数据时,新加数据使用的空间其实就是chain中的misalign空间,新建的chain的misalign空间就是全部的buffer内存,随着数据添加,misalign会越来越小。

Libevent学习参考链接

待续...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351