Libevent源码解读(三) |
您所在的位置:网站首页 › libevent2 › Libevent源码解读(三) |
Libevent源码解读(三)
事件主循环
事件处理主循环
IO和Timer 事件的统一
IO和signal事件的统一
集成信号处理
集成策略 --- socket pair
集成到事件主循环----通知event_base
evsignal_info结构体
注册 注销signal事件
集成定时器事件
集成到事件主循环
Timer小根堆
支持IO多路复用技术
事件主循环
根据系统提供的事件多路分发机制执行事件循环,堆已注册的就绪事件,调用注册事件的回调函数来处理事件 事件处理主循环Libevent 的事件主循环主要是通过event_base_loop()函数完成的,其主要操作入下面的流程图,event_base_loop所做的就是持续执行下面的循环 Libevent将信号事件和定时器事件都集成到了系统的IO的demultiplex机制中,首先将Timer事件的最小超时时间来设置系统IO的timeout时间:当系统IO返回时,在激活所有就绪的Timer事件就可以。这样就能将Timer事件完美融入系统的IO机制中。这是Reactor和Proactor模式的处理的经典方法 堆(小根堆)插入和删除的事件复杂度为LogN,N为堆中元素,二获取最小Key值为O(1) IO和signal事件的统一signal是异步事件的经典实例,如果当signal事件发生时,并不立即调用event的callback函数,而是设法通知系统IO机制,让其返回。然后在统一和IO事件以及Timer一起处理。 问题核心,当signal发生是如何通知系统的IO多路复用机制,比如pipe 总结:介绍了libevent是如何处理就绪的IO事件,定时器和信号事件 集成信号处理主要分析signal集成到事件主循环的框架中 集成策略 — socket pair可以实现上述在同一个文件描述符中进行读写的功能。该系统调用能创建一对已连接的UNIX族socket。在Linux中,完全可以把这一对socket当成pipe返回的文件描述符一样使用,唯一的区别就是这一对文件描述符中的任何一个都可读和可写,libevent使用的就是socket pair int socketpair(int d, int type, int protocol, int sv[2]);socketpair创建逻辑 注册signal事件是evsignal_add(struct event *ev)函数完成的,libevent对所有信号注册同一个处理函数evsignal_handler(),该函数将下一段介绍,注册过程如下: static void __cdecl evsig_handler(int sig) { int save_errno = errno; #ifdef _WIN32 int socket_errno = EVUTIL_SOCKET_ERROR(); #endif ev_uint8_t msg; if (evsig_base == NULL) { event_warnx( "%s: received signal %d, but have no base configured", __func__, sig); return; } //记录信号sig的触发次数,并且设置event触发标记 //evsignal_base->sig.evsigcaught[sig]++; //evsignal_base->sig.evsig_caught = 1; #ifndef EVENT__HAVE_SIGACTION signal(sig, evsig_handler); //重新注册信号 #endif /* Wake up our notification mechanism */ msg = sig; #ifdef _WIN32 send(evsig_base_fd, (char*)&msg, 1, 0); #else { int r = write(evsig_base_fd, (char*)&msg, 1); (void)r; /* Suppress 'unused return value' and 'unused var' */ } #endif errno = save_errno; #ifdef _WIN32 EVUTIL_SET_SOCKET_ERROR(socket_errno); #endif }总结:主要介绍了libevent对signal事件的具体处理框架,包括事件的注册,删除和socket pair通知机制,以及是如何将signal事件集成到事件主循环中 集成定时器事件相较于signal事件来说,Timer与IO的集成较为直观和简单 集成到事件主循环由于IO多路复用都允许一个最大等待时间(最大超时时间)timeout,即使事件没有发生都能够保证timeout事件内放回。因此根据小根堆的最小超时时间来设置超时时间:系统IO放回后,在激活所有的就绪的Timer时间 具体代码在源文件event.c的event_base_loop()中 if (!N_ACTIVE_CALLBACKS(base) && !(flags & EVLOOP_NONBLOCK)) { //根据Timer事件计算evsel->dispatch的最大等待时间 timeout_next(base, &tv_p); } else { /* * if we have active events, we just poll new events * without waiting. */ evutil_timerclear(&tv); } //调用select() or epoll_wait()等待IO事件 res = evsel->dispatch(base, tv_p); //处理超时事件,将超时事件插入到激活链表中 timeout_process(base); Timer小根堆Libevent使用堆来管理Timer事件,其key值就是事件超时时间,源代码位于min_heap.h中 支持IO多路复用技术Libevent的核心是事件驱动和同步非阻塞。为了达到这一目标。必须使用系统提供的IO多路复用 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |