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。