libevent源码学习-----event_base事件循环
2017-10-12 20:16
495 查看
event_base是libevent的事件驱动,也是Reactor模式的直接体现。任何使用libevent的代码最开始都需要创建一个base,之后的任何接口函数都和这个base关联着,下面是struct event_base的定义
struct event_base中主要包括上述变量,io多路复用操作,信号操作,各种map,队列,最小堆等等
其他的部分就是在程序设计过程中进行的各种判断,各种标志多一些
event_base的初始化是通过内部调用event_base_with_new_config实现的,函数意思是带有一些配置构造一个event_base,配置主要包括的就是用户不想要libevent使用的io复用函数名字。
函数中主要还是各种初始化,然后选择合适的io复用函数等
用户通过event_base_dispatch函数开启事件驱动的主循环,内部调用event_base_loop执行无线循环,也没有什么特别的
选择io复用函数的阻塞时长
调用io函数
判断最小堆中的event
处理所有激活的event
下面主要看一下如何处理激活event
函数处理当前队列中的每一个event,首先将其从激活队列中删除,如果是一次性事件,则从base中删除
然后就根据信号/其他永久io事件调用相应的处理函数
总结
event_base是整个事件驱动的载体,但是使用时仅仅需要event_base_new,和event_base_dispatch即可,内部实现了对事件的监控,对超时event的统一处理,对所有激活event按优先级处理,处理时再细分是永久/一次性event,io/signal event等。这种先统一再分散的好处是可以在一个函数中统一处理激活event,实现更好的封装接口,也更容易理清思路。
对信号的处理需要特别注意,因为不是一有信号发生马上调用用户的回调函数,中间其实进行了两次base循环,这是为了将信号统一到event的结果,不然就直接sigaction绑定不是很好?
具有超时时间的event在处理时也是需要注意的部分,需要重新计算超时时间,然后使用event_add_internal重新添加(event在timeout_process已经被删除了),这就回到了event_add调用的event_add_internal上,如果不是超时event就仅仅是调用回调函数而已。
整体思路很容易理清,重点是细节方面,需要仔细琢磨
struct event_base { /* io多路复用函数的统一接口 */ const struct eventop *evsel; /* io多路复用函数的数据 */ void *evbase; /* 信号处理的统一接口 */ const struct eventop *evsigsel; /* 信号处理的数据 */ struct evsig_info sig; /* 注册到base中的event,不包括内部event */ int event_count; /* 激活的event个数 */ int event_count_active; /* 存储套接字/描述符以及对应的事件的map */ struct event_io_map io; /* 存储信号的map */ struct event_signal_map sigmap; /* 注册队列 */ struct event_list eventqueue; /* 激活队列 */ struct event_list *activequeues; /* 最小堆 */ struct min_heap timeheap; /* ... */ };
struct event_base中主要包括上述变量,io多路复用操作,信号操作,各种map,队列,最小堆等等
其他的部分就是在程序设计过程中进行的各种判断,各种标志多一些
event_base的初始化是通过内部调用event_base_with_new_config实现的,函数意思是带有一些配置构造一个event_base,配置主要包括的就是用户不想要libevent使用的io复用函数名字。
函数中主要还是各种初始化,然后选择合适的io复用函数等
用户通过event_base_dispatch函数开启事件驱动的主循环,内部调用event_base_loop执行无线循环,也没有什么特别的
选择io复用函数的阻塞时长
调用io函数
判断最小堆中的event
处理所有激活的event
下面主要看一下如何处理激活event
/* * 由base主循环调用,用来处理在base的激活队列中的event, * 根据优先级遍历base的激活队列数组,对于每一个队列,调用event_process_active_single_queue处理 */ static int event_process_active(struct event_base *base) { /* Caller must hold th_base_lock */ struct event_list *activeq = NULL; int i, c = 0; for (i = 0; i < base->nactivequeues; ++i) { if (TAILQ_FIRST(&base->activequeues[i]) != NULL) { base->event_running_priority = i; activeq = &base->activequeues[i]; c = event_process_active_single_queue(base, activeq); } } return c; }
/* * 处理激活/超时event的主要函数 * 首先根据event是否是永久event判断是否需要将event从base的所有队列中删除 * 如果是永久队列,则只需要从base的激活队列中删除 * 然后才开始调用event的回调函数 * * 注意:此处需要处理具有超时时间的event,因为最小堆中记录的是绝对时间, * 所以如果这个event是具有超时时间的event,那么它是在从base中所有队列中删除后才加入到 * 激活队列中的,见timeout_process() * 所以需要再将其添加到base中,重新调用event_add_internal即可,见event_persist_closure * * 返回处理了多少个非内部event */ static int event_process_active_single_queue(struct event_base *base, struct event_list *activeq) { struct event *ev; int count = 0; for (ev = TAILQ_FIRST(activeq); ev; ev = TAILQ_FIRST(activeq)) { /* 如果是永久event,只需要从激活队列中删除 */ if (ev->ev_events & EV_PERSIST) event_queue_remove(base, ev, EVLIST_ACTIVE); /* 否则就要从base的所有队列中删除,包括激活队列, 因为只处理一次 */ else event_del_internal(ev); /* * 如果是信号或者永久event需要单独处理,而其他event在删除完之后直接调用回调函数就可以了 * 以后就不再管这个event了,因为只处理一次 * ev_closure用于判断是否是永久/信号event,在event_new中赋值 */ switch (ev->ev_closure) { case EV_CLOSURE_SIGNAL: /* 信号的特殊处理,调用用户的信号处理函数 */ event_signal_closure(base, ev); break; case EV_CLOSURE_PERSIST://io event /* 处理io事件,主要是特别处理有超时时间的event,需要重新计算绝对时间,然后调用回调函数 */ event_persist_closure(base, ev); break; } } return count; }
函数处理当前队列中的每一个event,首先将其从激活队列中删除,如果是一次性事件,则从base中删除
然后就根据信号/其他永久io事件调用相应的处理函数
总结
event_base是整个事件驱动的载体,但是使用时仅仅需要event_base_new,和event_base_dispatch即可,内部实现了对事件的监控,对超时event的统一处理,对所有激活event按优先级处理,处理时再细分是永久/一次性event,io/signal event等。这种先统一再分散的好处是可以在一个函数中统一处理激活event,实现更好的封装接口,也更容易理清思路。
对信号的处理需要特别注意,因为不是一有信号发生马上调用用户的回调函数,中间其实进行了两次base循环,这是为了将信号统一到event的结果,不然就直接sigaction绑定不是很好?
具有超时时间的event在处理时也是需要注意的部分,需要重新计算超时时间,然后使用event_add_internal重新添加(event在timeout_process已经被删除了),这就回到了event_add调用的event_add_internal上,如果不是超时event就仅仅是调用回调函数而已。
整体思路很容易理清,重点是细节方面,需要仔细琢磨
相关文章推荐
- 【libevent】源码学习(2)--配置event_base
- libevent源码学习(二)事件循环event_base
- Libevent之事件处理框架-event_base结构体学习
- libevent源码学习-----事件驱动流程分析
- libevent源码学习-----event操作
- libevent使用点滴(2)bufferevent_pair_new的事件时并不能使其循环
- jQuery源码学习(版本1.11)-事件处理-工具函数jQuery.event
- Libevent-2.1.8源码分析——event_base(一)
- libevent事件(二)---event源码
- Android事件分发详解(三)——ViewGroup的dispatchTouchEvent()源码学习
- libevent源码分析(6)--2.1.8--创建和释放libevent句柄event_base的相关函数
- libevent学习文档(二)eventbase相关接口和参数
- Python Tulip ( asyncio) 第2节 Base Event Loop 基本事件循环(2) 译文
- Libevent源码分析-----配置event_base
- muduo网络库学习(八)事件驱动循环线程池EventLoopThreadPool
- redis源码学习4 ae.c 事件循环
- BaseRecyclerViewAdapterHelper开源项目之点击事件源码学习
- Libevent源码分析-----配置event_base
- libevent源码深度剖析五 ——libevent的核心:事件event
- [libevent源码分析] event_base_dispatch