libevent源码学习_event结构体
在libevent中最重要的结构体莫过于event和event_base了,下面对于这2个结构体进行分析。
1、结构体event,位于:event.h
1 struct event { 2 /* 3 * 双向链表节点指针 4 * 是libevent对不同事件类型和在不同的时期 对事件的管理时使用到的字段 5 */ 6 /*增加下一个事件*/ 7 TAILQ_ENTRY (event) ev_next; 8 /*增加下一个活动事件*/ 9 TAILQ_ENTRY (event) ev_active_next; 10 /*增加下一个信号*/ 11 TAILQ_ENTRY (event) ev_signal_next; 12 13 /* 14 * timeout事件 表示该event保存在min_heap数组中的索引 15 */ 16 /* for managing timeouts */ 17 unsigned int min_heap_idx; 18 19 /* 20 * 该事件所属的反应堆实例 21 * 一个全局变量,管理着事件,如事件总数,事件列表 22 */ 23 struct event_base *ev_base; 24 25 /* 26 * 对于I/O事件,是绑定的文件描述符 27 * 对于signal事件,是绑定的信号 28 */ 29 int ev_fd; 30 31 /* 32 * event关注的事件类型,它可以是以下3种类型: 33 * I/O事件:EV_WRITE和EV_READ 34 * 定时事件:EV_TIMEOUT 35 * 信号:EV_SIGNAL 36 * 辅助选项:EV_PERSIST,表明是一个永久事件 37 */ 38 short ev_events; 39 40 /* 41 * 事件就绪执行时,调用ev_callback的次数,通常为1 42 */ 43 short ev_ncalls; 44 45 /* 46 * 指针,通常指向ev_ncalls或者为NULL 47 */ 48 /* Allows deletes in callback */ 49 short *ev_pncalls; 50 51 /* 52 * timeout事件 超时值 53 */ 54 struct timeval ev_timeout; 55 56 /* 57 * 优先级,越小越高 58 */ 59 /* smaller numbers are higher priority */ 60 int ev_pri; 61 62 /* 63 * event的回调函数,被ev_base调用,执行事件处理程序 64 * 各参数说明如下: 65 * 参数1: ev_fd,表示触发事件的文件句柄,比如一个socket对象 66 * 参数2: ev_events,表示事件发生的结果,可能是超时、可读、可写 67 * 参数3: ev_arg,表示传递给事件的参数 68 */ 69 void (*ev_callback)(int, short, void *arg); 70 71 /* 72 * 回调函数的参数 73 */ 74 void *ev_arg; 75 76 /* 77 * 记录了当前激活事件的类型 78 */ 79 /* result passed to event callback */ 80 int ev_res; 81 82 /* 83 * libevent用于标记event信息的字段,表明其当前的状态 84 */ 85 int ev_flags; 86 };
2、结构体event_base,位于:event_internal.h
1 struct event_base { 2 /* 3 * 表示选择的事件引擎,可能为: epoll, poll, select 4 */ 5 const struct eventop *evsel; 6 7 /* 8 * 全局对象,evbase实际上是一个eventop实例对象,执行具体任务 9 * 在函数event_base_new()中被初始化base->evbase = base->evsel->init(base) 10 */ 11 void *evbase; 12 13 /* counts number of total events */ 14 int event_count; 15 16 /* counts number of active events */ 17 int event_count_active; 18 19 /* Set to terminate loop */ 20 int event_gotterm; 21 22 /* Set to terminate loop immediately */ 23 int event_break; 24 25 /* 26 * libevent支持事件优先级,因此可以把它看作是数组 27 * 其中的元素activequeues[priority]是一个链表 28 * 链表的每个节点指向一个优先级为priority的就绪事件event 29 */ 30 /* active event management */ 31 struct event_list **activequeues; 32 33 int nactivequeues; 34 35 /* 36 * 用来管理信号的结构体 37 */ 38 /* signal handling info */ 39 struct evsignal_info sig; 40 41 /* 42 * 链表,保存了所有的注册事件event的指针 43 */ 44 struct event_list eventqueue; 45 46 /* 47 * 系统的当前时间 48 */ 49 struct timeval event_tv; 50 51 /* 52 * 管理定时事件的小根堆 53 */ 54 struct min_heap timeheap; 55 56 /* 57 * 与event::ev_timeout进行比较,确定事件是否超时 58 */ 59 struct timeval tv_cache; 60 };
3、结构体eventop,位于:event_internal.h
1 struct eventop { 2 const char *name; 3 4 /* 5 * 初始化 6 */ 7 void *(*init)(struct event_base *); 8 9 /* 10 * 注册事件 11 */ 12 int (*add)(void *, struct event *); 13 14 /* 15 * 删除事件 16 */ 17 int (*del)(void *, struct event *); 18 19 /* 20 * 事件分发 21 */ 22 int (*dispatch)(struct event_base *, void *, struct timeval *); 23 24 /* 25 * 注销,释放资源 26 */ 27 void (*dealloc)(struct event_base *, void *); 28 29 /* set if we need to reinitialize the event base */ 30 int need_reinit; 31 };
实际真正使用这个结构体是在event.c中,根据系统支持的不同系统调用来选取一组上述的eventop操作,这是用C语言模拟多态的典型用法。
1 /* In order of preference */ 2 static const struct eventop *eventops[] = { 3 #ifdef HAVE_EVENT_PORTS 4 &evportops, 5 #endif 6 #ifdef HAVE_WORKING_KQUEUE 7 &kqops, 8 #endif 9 #ifdef HAVE_EPOLL 10 &epollops, 11 #endif 12 #ifdef HAVE_DEVPOLL 13 &devpollops, 14 #endif 15 #ifdef HAVE_POLL 16 &pollops, 17 #endif 18 #ifdef HAVE_SELECT 19 &selectops, 20 #endif 21 #ifdef WIN32 22 &win32ops, 23 #endif 24 NULL 25 };
有了上面的定义,比如我们系统使用epoll相关的系统调用,则会有对应的一组函数,位于:epoll.c
1 const struct eventop epollops = { 2 "epoll", 3 epoll_init, 4 epoll_add, 5 epoll_del, 6 epoll_dispatch, 7 epoll_dealloc, 8 1 /* need reinit */ 9 };
上面提到的这些结构体对应的UML图如下:
从这个关系中我们可以很容易的看出他们在Reactor模型中各自代表了什么。
先看来一下Reactor模式的UML图:
图1
图2
在Reactor模式中,有5个关键的参与者:
- 描述符(handle):由操作系统提供的资源,用于识别每一个事件,如Socket描述符、文件描述符、信号的值等。在Linux中,它用一个整数来表示。事件可以来自外部,如来自客户端的连接请求、数据等。事件也可以来自内部,如信号、定时器事件。
- 同步事件多路分离器(event demultiplexer):事件的到来是随机的、异步的,无法预知程序何时收到一个客户连接请求或收到一个信号。所以程序要循环等待并处理事件,这就是事件循环。在事件循环中,等待事件一般使用I/O复用技术实现。在linux系统上一般是select、poll、epol_waitl等系统调用,用来等待一个或多个事件的发生。I/O框架库一般将各种I/O复用系统调用封装成统一的接口,称为事件多路分离器。调用者会被阻塞,直到分离器分离的描述符集上有事件发生。
- 事件处理器(event handler):I/O框架库提供的事件处理器通常是由一个或多个模板函数组成的接口。这些模板函数描述了和应用程序相关的对某个事件的操作,用户需要继承它来实现自己的事件处理器,即具体事件处理器。因此,事件处理器中的回调函数一般声明为虚函数,以支持用户拓展。
- 具体的事件处理器(concrete event handler):是事件处理器接口的实现。它实现了应用程序提供的某个服务。每个具体的事件处理器总和一个描述符相关。它使用描述符来识别事件、识别应用程序提供的服务。
- Reactor 管理器(reactor):定义了一些接口,用于应用程序控制事件调度,以及应用程序注册、删除事件处理器和相关的描述符。它是事件处理器的调度核心。 Reactor管理器使用同步事件分离器来等待事件的发生。一旦事件发生,Reactor管理器先是分离每个事件,然后调度事件处理器,最后调用相关的模 板函数来处理这个事件。
通过上面2张Reactor的UML图及描述,可以知道在libevent里面结构体和Reactor的对应关系:
- 描述符(handle):对于I/O事件就是socket,对于信号就是绑定的信号。
- 同步事件多路分离器(event demultiplexer):对应epoll、select、poll、kqueue、evport、devpoll等不同的系统调用。
- 事件处理器(event handler):对应结构体event,该结构体中的成员void (*ev_callback)(int, short, void *arg)就是回调函数的模板,也就是面向对象中的虚函数。
- 具体的事件处理器(concrete event handler):对应的具体回调函数都是通过函数event_set()去设置的。
- Reactor 管理器(reactor):对应结构体event_base,里面根据系统选择系统调用的一组函数进行注册,并dispatch分发。
图3
本文参考自:
http://blog.csdn.net/sparkliang/article/details/4957744 (图1)
http://blog.csdn.net/u013074465/article/details/46276967 (图2及原始Reactor模型的描述)
http://blog.csdn.net/chinabhlt/article/details/43452977(图3)
posted on 2017-09-27 16:24 LastBattle 阅读(634) 评论(0) 编辑 收藏 举报