libevent源码I---hello-word

 

struct event_base* base = event_init();

函数event_init();

该函数创建了一个event_base对象,一个event_base对象相当于一个Reactor实例。

struct event_base *
event_init(void)
{
    struct event_base *base = event_base_new();    //event_init()调用event_base_new()
    if (base != NULL)
        current_base = base;
    return (base);
}
struct event_base* event_init(void)
{
    struct event_base *base = event_base_new_with_config(NULL);
    
    if(base == NULL)
    {
        event_errx(1,"%s, Unable to construct event_base", __func__);
        return NULL;
    }

    current_base = base;

    return(base);
}

我们发现event_init()工作量很少,只是调用event_base_new()函数,并将该对象赋值给全局变量current_base,作为全局默认的event_base。所以真正初始化event_base的工作是在event_base_new()函数内完成。    
   

struct event_base *
event_base_new(void)    //初始化libevent的event_base
{
    int i;
    struct event_base *base;
    if ((base = calloc(1, sizeof(struct event_base))) == NULL)    //在堆上分配内存存储event_base,所有字段初始化为0
        event_err(1, "%s: calloc", __func__);
    event_sigcb = NULL;
    event_gotsig = 0;
    detect_monotonic();    //设置use_monotonic变量
    gettime(base, &base->event_tv);    //base->tv_cache.tv_sec非0,则赋给base->event_tv
    
    min_heap_ctor(&base->timeheap);    //初始化定时事件的小根堆base->timeheap    min_heap.h
    TAILQ_INIT(&base->eventqueue);    //初始化注册事件链表base->eventqueue    sys/queue.h
    base->sig.ev_signal_pair[0] = -1;    //初始化信号base->sig
    base->sig.ev_signal_pair[1] = -1;
    
    base->evbase = NULL;    //初始化I/O多路复用 base->evbase
    //遍历全局数组eventops[],初始化libevent的I/O多路复用机制
    for (i = 0; eventops[i] && !base->evbase; i++) {    //以NULL标志数组结尾,只选取一个I/O多路复用机制
        base->evsel = eventops[i];    //初始化base->evsel
        base->evbase = base->evsel->init(base);    //初始化base->evbase
    }
    if (base->evbase == NULL)    //没有I/O多路复用
        event_errx(1, "%s: no event mechanism available", __func__);
    if (evutil_getenv("EVENT_SHOW_METHOD")) //调用getenv()获取环境变量EVENT_SHOW_METHOD    evutil.c
        event_msgx("libevent using: %s\n",
               base->evsel->name);
    /* allocate a single active event queue */
    //event_base_new()内调用event_base_priority_init()
    event_base_priority_init(base, 1);    //设置优先级base->nactivequeues;分配数组base->activequeues。数组大小和优先级相同
    return (base);
}

其中由3点需要注意:

1.该函数调用calloc()在堆上分配内存来存储event_base;

2.使用全局数组eventops[]存储系统支持的I/O多路复用机制,然后遍历该数组,选取第1个I/O多路复用机制。

3.libevent支持event有优先级,所以又调用了event_base_priority_init()来完成优先级相关的设置。

//设置不同event的优先级,值越小,优先级越高
//返回值:0,成功;-1,出错
int
event_base_priority_init(struct event_base *base, int npriorities)
{
    int i;
    if (base->event_count_active)    //当前base上有活跃的events则不能设置优先级,返回。
        return (-1);
    if (npriorities == base->nactivequeues)    //设置的优先级和当前优先级相同,则直接返回
        return (0);
    if (base->nactivequeues) {    //不同,则先释放原先的activequeues数组
        for (i = 0; i < base->nactivequeues; ++i) {
            free(base->activequeues[i]);
        }
        free(base->activequeues);
    }
    /* Allocate our priority queues */
    base->nactivequeues = npriorities;    //设置新的优先级
    base->activequeues = (struct event_list **)
        calloc(base->nactivequeues, sizeof(struct event_list *));    //设置和优先级值相同大小的event_list数组
    if (base->activequeues == NULL)
        event_err(1, "%s: calloc", __func__);
    for (i = 0; i < base->nactivequeues; ++i) {
        base->activequeues[i] = malloc(sizeof(struct event_list));    //初始化activequeues数组中每个元素
        if (base->activequeues[i] == NULL)
            event_err(1, "%s: malloc", __func__);
        TAILQ_INIT(base->activequeues[i]);
    }
    return (0);
}

该函数设置优先级,初始化了event_base的nactivequeues成员和activequeues成员。优先级值越小,优先级越高。在活跃事件链表中,优先级高的event先被处理。

 

结构体event_base

 struct event_base {
        const struct eventop *evsel;
        void                *evbase;
        int                  event_count;        /* counts number of total events */
        int                  event_count_active;/* counts number of active events */
        int                  event_gotterm;        /* Set to terminate loop */
        int                  event_break;        /* Set to terminate loop immediately */
        struct event_list  **activequeues;
        int                  nactivequeues;
        struct evsignal_info sig;
        struct event_list    eventqueue;
        struct timeval       event_tv; 
        struct min_heap      timeheap;
        struct timeval       tv_cache;
    };

 

  evsel:    当前使用的系统IO事件模型的抽象接口,event_base_new()函数会
                 按顺序检测可用的IO事件模型,该顺序为:
                  event ports, kqueue, epool, /dev/poll, poll, select.
    evbase:   evsel->init()的执行结果。在event_base_new()中,使用evsel->init()
                    的返回值判断特定的IO事件模型是否可用。(返回NULL则不可用)
    event_count:    当前event base中包含的事件数。
    event_count_active: 当前event base中活动的事件数。
    activequeues: 活动事件的队列。
    nactivequeues: activequeues的长度
    eventqueue:   当前event base中包含所有event的列表。
    event_tv:    在event_base_new中,设置为当前时间。
    timeheap:     libevent库使用一个最小堆结构,保存各个事件的超时时间,
                         从而可以优化超时判断的性能。

 

问题:1、函数的返回值是结构体指针与结构体有什么不同?

当被调用的子函数返回值为结构体的时候,调用函数将分配一段空间用于存放返回的结构体(使用一个结构体变量接受返回值),并将这段空间的地址作为调用时的参数压栈。子程序不负责对要返回的结构体分配空间。最后返回eax中存放的是结构体空间(栈中)的地址。在子程序退出的时候,调用函数可以在自己的栈帧中访问到返回的值。

子程序填充malloc在堆中生成的结构体空间,并将其地址存放在eax中返回。但是这种使用方式存在的很大问题是在子程序中使用到了malloc但是没有与之对应的free,如果在调用函数中忽视释放操作的话将会导致堆内存的泄露。当然在C++中可以使用构造函数和析构函数处理这些细节。

--------------------- 
作者:huizhang0110 
来源:CSDN 
原文:https://blog.csdn.net/dfq12345/article/details/73924580 

2、在event_base_new函数中,先给结构体分配内存空间。

答案看问题1。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。著名分布式缓存软件memcached也是libevent based,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。 编辑本段 详细   编译库代码,编译脚本会判断OS支持哪种类型的事件机制(select、epoll或kqueue),然后条件编译相应代码,供上层使用的接口仍然是保持统一的(否则也不能所谓的跨平台了)。在linux redhat as 4 u 2 上编译相当容易,configure以后make,make install就可以了,windows上编译似乎有点小麻烦,不过稍微改点东西也就通过了。   从代码中看,libevent支持用户使用三种类型的事件,分别是网络IO、定时器、信号三种,在定时器的实现上使用了RB tree的数据结构,以达到高效查找、排序、删除定时器的目的,网络IO上,主要关注了一下linux上的epoll(因为目前的开发主要在linux平台),结果发现libevent的epoll居然用的EPOLLLT,水平触发的方式用起来比较方便,不容易出错,但是在效率上可能比EPOLLET要低一些。   跟网络无关的,libevent也有一些缓冲区管理的函数,而且是c风格的函数,实用性不是太大。libevent没有提供缓存的函数。   虽然libevent实用上的价值不大,但它提供的接口形式还是不错的,实现类似的lib的时候仍然是可以参考的。   Libevent定时器的数据结构自version 1.4起已由红黑树改为最小堆(Min Heap),以提高效率;网络IO和信号的数据结构采用了双向链表(TAILQ)。在实现上主要有3种链表: EVLIST_INSERTED, EVLIST_ACTIVE, EVLIST_TIMEOUT,一个ev在这3种链表之间被插入或删除,处于EVLIST_ACTIVE链表中的ev最后将会被调度执行。   Libevent提供了DNS,HTTP Server,RPC等组件,HTTP Server可以说是Libevent的经典应用。从http.c可看到Libevent的很多标准写法。写非阻塞式的HTTP Server很容易将socket处理与HTTP协议处理纠缠在一起,Libevent在这点上似乎也有值得推敲的地方。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值