Libevent源码解读(三)

您所在的位置:网站首页 libevent2 Libevent源码解读(三)

Libevent源码解读(三)

#Libevent源码解读(三)| 来源: 网络整理| 查看: 265

Libevent源码解读(三) 事件主循环 事件处理主循环 IO和Timer 事件的统一 IO和signal事件的统一 集成信号处理 集成策略 --- socket pair 集成到事件主循环----通知event_base evsignal_info结构体 注册 注销signal事件 集成定时器事件 集成到事件主循环 Timer小根堆 支持IO多路复用技术

事件主循环

根据系统提供的事件多路分发机制执行事件循环,堆已注册的就绪事件,调用注册事件的回调函数来处理事件

事件处理主循环

Libevent 的事件主循环主要是通过event_base_loop()函数完成的,其主要操作入下面的流程图,event_base_loop所做的就是持续执行下面的循环

在这里插入图片描述

IO和Timer 事件的统一

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创建逻辑 在这里插入图片描述

listener = socket(AF_INET, type, 0); if (listener event_warn("epoll_wait"); return (-1); } evsignal_process(base);//处理信号事件 return (0); }else if(base->sig.evsignal_caught) evsignal_process(base);//处理信号事件

在这里插入图片描述

evsignal_info结构体 /* Data structure for the default signal-handling implementation in signal.c */ struct evsig_info { /* Event watching ev_signal_pair[1] */ struct event ev_signal; //为socketpair的读socket向event_base注册读事件时使用的event结构体 /* Socketpair used to send notifications from the signal handler */ evutil_socket_t ev_signal_pair[2]; /* True iff we've added the ev_signal event yet. */ int ev_signal_added; /* Count of the number of signals we're currently watching. */ int ev_n_signals_added; /* Array of previous signal handler objects before Libevent started * messing with them. Used to restore old signal handlers. */ #ifdef EVENT__HAVE_SIGACTION struct sigaction **sh_old; #else ev_sighandler_t **sh_old; #endif /* Size of sh_old. */ int sh_old_max; }; 注册 注销signal事件

注册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