您的位置:首页 > 其它

libevent的大框框

2016-03-13 16:02 162 查看
libevent库(框架)主要统一管理三类事件,timeout事件,io事件,signal事件。

利用各种数据结构构建了一些容器(链表,最小堆等),将各种事件按逻辑放在这些容器(common_timeout_queues,event_signal_map,event_io_map,eventqueue ,activequeues等)中。

建立了消息循环,其中监察者从已注册事件容器(eventqueue )中轮询各个事件触发条件是否达成,若是触发条件成立,则将事件从已注册事件容器(eventqueue )中删除,放入已触发事件容器(activequeues)中,执行者从已触发事件容器拿取事件执行事件回调过程,并根据事件类型做后续处理。

//--------------------------------------------------------------------------------------------------------------------

timeout事件

挂载在 base->common_timeout_queues和 eventqueue 上,common_timeout_queues是一个二维表 ,横向是超时时间,纵向是相应超时事件注册的事件队列。

下面是一个用tailqueue尾队列(双向链表)写的二维表的demo(在linux下用gcc编译运行),queue.h头文件在/usr/include/sys/queue.h

横坐标是department部门,纵向是person。

#include <stdio.h>
#include <stdlib.h>
#include <sys/queue.h>

struct person{
char pername[20];
TAILQ_ENTRY(person) entries;
};

struct department{
char depname[20];
TAILQ_HEAD(, person) persons;
};

struct company{
struct department **deps;
int ndeps;
};

#define DEPARTMENT_NUM 5
#define PERSON_NUM 10

int main()
{
int i,j;
struct company comp;

comp.ndeps = DEPARTMENT_NUM;

comp.deps = malloc(sizeof(struct department*)*comp.ndeps);

for(i=0; i<comp.ndeps; i++){

comp.deps[i] = (struct department*)malloc(sizeof(struct department));
sprintf(comp.deps[i]->depname,"dep %d",i);
TAILQ_INIT(&(comp.deps[i]->persons));

for(j=0; j<PERSON_NUM; j++){
struct person *per = malloc(sizeof(struct person));
sprintf(per->pername,"per name %d %d",i,j);
TAILQ_INSERT_TAIL(&(comp.deps[i]->persons),per,entries);
}
}

struct person *item;
for(i=0; i<comp.ndeps; i++){

printf("\n%s\n",comp.deps[i]->depname);

TAILQ_FOREACH(item, &(comp.deps[i]->persons), entries){
printf("%s\n",item->pername);
}

}

for(i=0; i<comp.ndeps; i++){

while(!TAILQ_EMPTY(&(comp.deps[i]->persons))){
struct person *tmp = TAILQ_FIRST(&(comp.deps[i]->persons));
TAILQ_REMOVE(&(comp.deps[i]->persons), tmp, entries);
free(tmp);
}

free(comp.deps[i]);
}

free(comp.deps);

}


下面是一个用strcut list_head(双向链表,常用于linux内核中,libevent没用到)写的二维表的demo

#include <stdio.h>
#include <stdlib.h>

#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

#define container_of(ptr, type, member) ({      \
const typeof( ((type *)0)->member ) *__mptr = (ptr);  \
(type *)( (char *)__mptr - offsetof(type,member) );})

#define _LIST_FOREACH(item, head) \
for ((item) = (head)->next; (item) != (head); (item) = (item)->next)

#define _LIST_EMPTY(head) ((head) == (head)->next)
#define _LIST_FIRST(head) ((head)->next)
#define _LIST_TAIL(head) ((head)->prev)

#define _LIST_INIT(head) do{ \
(head)->next = (head);    \
(head)->prev = (head);    \
} while(0)

#define _LIST_INSERT_TAIL(head, item) do{ \
(item)->next = (head);        \
(item)->prev = (head)->prev;  \
(head)->prev->next = (item);  \
(head)->prev = (item);        \
} while(0)

#define _LIST_REMOVE(item)  do{ \
(item)->next->prev = (item)->prev;  \
(item)->prev->next = (item)->next;  \
} while(0)

struct list_head{
struct list_head *next,*prev;
};

struct person{
char pername[20];
struct list_head entries;
};

struct department{
char depname[20];
struct list_head persons;
};

struct company{
struct department **deps;
int ndeps;
};

#define DEPARTMENT_NUM 5
#define PERSON_NUM 10

int main()
{
int i,j;
struct company comp;

comp.ndeps = DEPARTMENT_NUM;

comp.deps = malloc(sizeof(struct department*)*comp.ndeps);

for(i=0; i<comp.ndeps; i++){

comp.deps[i] = (struct department*)malloc(sizeof(struct department));
sprintf(comp.deps[i]->depname,"dep %d",i);
_LIST_INIT(&(comp.deps[i]->persons));

for(j=0; j<PERSON_NUM; j++){
struct person *per = malloc(sizeof(struct person));
sprintf(per->pername,"per name %d %d",i,j);
_LIST_INSERT_TAIL(&(comp.deps[i]->persons),&(per->entries));
}
}

struct person *tmp;
struct list_head *item;
for(i=0; i<comp.ndeps; i++){

printf("\n%s\n",comp.deps[i]->depname);

_LIST_FOREACH(item, &(comp.deps[i]->persons)){
tmp = container_of(item,struct person, entries);
printf("%s\n",tmp->pername);
}

}

for(i=0; i<comp.ndeps; i++){

while(!_LIST_EMPTY(&(comp.deps[i]->persons))){
struct list_head *list_tmp = _LIST_FIRST(&(comp.deps[i]->persons));
tmp = container_of(list_tmp,struct person, entries);
_LIST_REMOVE(list_tmp);
free(tmp);
}

free(comp.deps[i]);
}

free(comp.deps);

}


主循环中的timeout_process函数(监察者)会判断超时事件是否超时, 若是超时则将事件从common_timeout_queues和 eventqueue中删除 ,并挂载到 activequeues上,等待统一处理。

主循环event_process_active函数(执行者)会循环处理挂载在base->activequeues队列上的事件,并作相应的后续处理。

//---------------------------------------------------------------------------

signal 事件 

首先 signal 是全局的,数量和条目是固定的,所以不存在自己创造一个,不同于io事件和timeout事件,它们是任意的当然是有穷多个的。

evsig_init 函数创建 base->sig.ev_signal_pair (实际是一对sokect文件描述符),并设置其 o_nonblock和fd_cloexec 标志位 ,用创建的文件描述符向event_base注册一个内部io读事件,这个事件一直等待,触发后调用 base->sig.ev_signal回调函数 evsig_cb读 ev_signal_pair 并且根据读的内容激活
base->sigmap 中相应的事件。

signal 的operations 只有两种 evsig_add调用和 evsig_del调用,其实质是用 signal函数或者sigaction函数向系统注册或注销信号回调函数evsig_handler,此回调函数往 base->sig.ev_signal_pair[0] (实际是sokect文件描述符) 中写数据,用以触发初始化时注册的io读事件。

struct event_signal_map 实际上是一个二维表 ,横向坐标是信号的数值,纵向是相应信号上注册事件的队列。

实际上信号事件被映射成了io读的事件

//----------------------------------------------------------------------

io 事件 

struct event_io_map == event_signal_map 实际上是一个二维表 ,横向坐标是打开的文件描述符,纵向是相应文件描述符上注册的事件队列。

base->evsel以 select 为例,注册事件的读写文件描述符会被添加到 readset 或者 writeset(全局)中去,且事件会被挂载到base->io二维表上去,同时挂载到eventqueue上,在主循环的event_base_loop函数中,res = evsel->dispatch(base, tv_p)会调用select系统调用,参数为全局的 readset 和writeset ,当select返回后,遍历base->io

二维表,将就绪的io事件从eventqueue中删除并添加到 activequeu
4000
es中间去,等待统一处理。

//-------------------------------------------------------------------------

事件状态机,挂在eventqueue上面是 enabled 状态,挂在activequeues 是被激活等待执行

状态,还有许多小状态机。状态转移的触发条件是select返回 ,signal产生和轮询超时。

首先简化了程序员的工作量,不用关心一些细节,更多的关注业务逻辑,关注设计。

统一事件源 , 不用一个fd调用用一个select 或者poll 等io复用函数,而是所有fd 用一个select或者poll 等io复用函数,不会多次触发内核异常,使上下文多次从用户态转入内核态,增加效率 。 

signal 有点类似于单片机的中断 ,在linux驱动设计中中断中要做尽量少的工作(尽量快速),且不能调用引起等待或挂起的函数(引起死循环),中断分上下两部分 , 此处类似于这个设计,在signal回调中干少量工作 ,只是通知有信号产生(上中断) ,真正的工作在主循环中执行(下中断),而且在signal回调函数中有许多调用是不安全的,且信号会中断程序执行,且信号不会计数产生次数。

统一时钟源,便于统一管理,提高实效。

值得学习的地方:尾队列使用 ,singal和io对event 的映射,eventqueue和activequeues两个队列的设计。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: