libevent是一个事件触发的网络库,适用于windows、linux、bsd等多种平台,内部使用select、epoll、kqueue等系统调用管理事件机制。著名的用于apache的php缓存库memcached据说也是libevent based,而且libevent在使用上可以做到跨平台,而且根据libevent官方网站上公布的数据统计,似乎也有着非凡的性能。
编译库代码,编译脚本会判断OS支持哪种类型的事件机制(select、epoll或kqueue),然后条件编译相应代码,供上层使用的接口仍然是保持统一的。
libevent支持用户使用三种类型的事件,分别是网络IO、定时器、信号三种,在定时器的实现上使用了RB tree的数据结构,以达到高效查找、排序、删除定时器的目的,网络IO上,主要关注了一下linux上的epoll(因为目前的开发主要在linux平台),结果发现libevent的epoll居然用的EPOLLLT,水平触发的方式用起来比较方便,不容易出错,但是在效率上可能比EPOLLET要低一些。
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在这点上似乎也有值得推敲的地方。
eventop结构体是所有事件驱动模型的基类。所有的io复用类型都会实现此结构体里各种方法
struct eventop { const char *name; ///<事件驱动名称 void *(*init)(struct event_base *); //<初始化 int (*add)(void *, struct event *); ///<加入新的事件监测 int (*del)(void *, struct event *); ///<从事件监测中删除某一事件 int (*dispatch)(struct event_base *, void *, struct timeval *);///<启动此事件监测 void (*dealloc)(struct event_base *, void *); ///<释放此事件驱动的资源 /* set if we need to reinitialize the event base */ int need_reinit; ///<标志位 }; |
event_base管理所有的event对象,它包含了一些全部变量,比如事件驱动引擎evsel等。所有的event对象都会包含这个结构体。
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 */ /* active event management */ struct event_list **activequeues; ///<激活队列 int nactivequeues; ///<激活队列数目 /* signal handling info */ struct evsignal_info sig; ///<信号 struct event_list eventqueue; ///<全部事件队列 struct timeval event_tv; struct min_heap timeheap; ///<这里libevent将定时器队列实现为一个最小堆,也就是为了每次都把时间最晚的定时器能取出来,然后实现超时。 struct timeval tv_cache; }; |
event结构表示每个事件,包含了一些事件私有的数据,比如回调函数等。。这里事件链表它使用了tail queue
struct event { TAILQ_ENTRY (event) ev_next; ///<下一个事件 TAILQ_ENTRY (event) ev_active_next; ///<下一个激活事件 TAILQ_ENTRY (event) ev_signal_next; ///<下一个信号事件列表 unsigned int min_heap_idx; /* for managing timeouts */ struct event_base *ev_base; ///<全局的base int ev_fd; ///<所需要监测的事件句柄 short ev_events; short ev_ncalls; short *ev_pncalls; /* Allows deletes in callback */ struct timeval ev_timeout; ///<超时时间 int ev_pri; /* smaller numbers are higher priority */ void (*ev_callback)(int, short, void *arg); ///<回调函数 void *ev_arg; ///<传递给回调函数的参数 int ev_res; /* result passed to event callback */ int ev_flags; }; |
事件引擎中的两个结构,这里主要介绍下select,其他的类似.
selectop是全局的select数据结构,也就是上面event_base数据结构中evbase的值,通过avbase就可以操作select的数据结构
struct selectop { int event_fds; /* Highest fd in fd set */ int event_fdsz; fd_set *event_readset_in; fd_set *event_writeset_in; fd_set *event_readset_out; fd_set *event_writeset_out; struct event **event_r_by_fd; struct event **event_w_by_fd; };
selectops也就是实现了eventop。它导出了select的接口。 const struct eventop selectops = { "select", select_init, select_add, select_del, select_dispatch, select_dealloc, 0 }; |
libevent中几个核心函数:
event_set(&c->event, sfd, event_flags, event_handler, (void *)c)
把sfd这个文件描述符放入c->event,并且告知当事件event_flags发生时回调event_handler,并以c为回调参数。
event_add(&ev, NULL)
把ev注册到事件队列里面,第二个参数指定的是超时值,设定成NULL表示忽略这项设定。
event_dispatch()
表示进入事件循环,当队列里任何一个文件描述符发生事件的时候就会执行回调函数。
---------------------------------
更多的参看
阅读(2265) | 评论(0) | 转发(0) |