您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 质量控制/管理 > libevent全面源码探究
Libevent全面源码探究张亮1前言Libevent是一个轻量级的开源高性能网络库,使用者众多,研究者更甚,相关文章也不少。写这一系列文章的用意在于,一则分享心得;二则对libevent代码和设计思想做系统的、更深层次的分析,写出来,也可供后来者参考。附带一句:Libevent是用c语言编写的(MS大牛们都偏爱c语言哪),而且几乎是无处不函数指针,学习其源代码也需要相当的c语言基础。Libevent简介上来当然要先夸奖啦,Libevent有几个显著的亮点:事件驱动(event-driven),高性能;轻量级,专注于网络,不如ACE那么臃肿庞大;源代码相当精炼、易读;跨平台,支持Windows、Linux、*BSD和MacOs;支持多种I/O多路复用技术,epoll、poll、dev/poll、select和kqueue等;支持I/O,定时器和信号等事件;注册事件优先级;Libevent已经被广泛的应用,作为底层的网络库;比如memcached、Vomit、Nylon、Netchat等等。Libevent当前的最新稳定版是1.4.13;这也是本文参照的版本。学习的好处学习libevent有助于提升程序设计功力,除了网络程序设计方面外,Libevent的代码里有很多有用的设计技巧和基础数据结构,比如信息隐藏、函数指针、c语言的多态支持、链表和堆等等,都有助于提升自身的程序功力。程序设计不止要了解框架,很多细节之处恰恰也是事关整个系统成败的关键。只对libevent本身的框架大概了解,那或许仅仅是一知半解,不深入代码分析,就难以了解其设计的精巧之处,也就难以为自己所用。事实上Libevent本身就是一个典型的Reactor模型,理解Reactor模式是理解libevent的基石;因此下一节将介绍典型的事件驱动设计模式——Reactor模式。参考资料:Libevent:~provos/libevent/Reactor模式前面讲到,整个libevent本身就是一个Reactor,因此本节将专门对Reactor模式进行必要的介绍,并列出libevnet中的几个重要组件和Reactor的对应关系,在后面的章节中可能还会提到本节介绍的基本概念。1Reactor的事件处理机制首先来回想一下普通函数调用的机制:程序调用某函数?函数执行,程序等待?函数将结果和控制权返回给程序?程序继续处理。Reactor释义“反应堆”,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。使用Libevent也是想Libevent框架注册相应的事件和回调函数;当这些时间发声时,Libevent会调用这些回调函数处理相应的事件(I/O读写、定时和信号)。用“好莱坞原则”来形容Reactor再合适不过了:不要打电话给我们,我们会打电话通知你。举个例子:你去应聘某xx公司,面试结束后。“普通函数调用机制”公司HR比较懒,不会记你的联系方式,那怎么办呢,你只能面试完后自己打电话去问结果;有没有被录取啊,还是被据了;“Reactor”公司HR就记下了你的联系方式,结果出来后会主动打电话通知你:有没有被录取啊,还是被据了;你不用自己打电话去问结果,事实上也不能,你没有HR的留联系方式。2Reactor模式的优点Reactor模式是编写高性能网络服务器的必备技术之一,它具有如下的优点:1)响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;3)可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;4)可复用性,reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;3Reactor模式框架使用Reactor模型,必备的几个组件:事件源、Reactor框架、多路复用机制和事件处理程序,先来看看Reactor模型的整体框架,接下来再对每个组件做逐一说明。1)事件源Linux上是文件描述符,Windows上就是Socket或者Handle了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,比如I/O事件。2)eventdemultiplexer——事件多路分发机制由操作系统提供的I/O多路复用机制,比如select和epoll。程序首先将其关心的句柄(事件源)及其事件注册到eventdemultiplexer上;当有事件到达时,eventdemultiplexer会发出通知“在已经注册的句柄集中,一个或多个句柄的事件已经就绪”;程序收到通知后,就可以在非阻塞的情况下对事件进行处理了。对应到libevent中,依然是select、poll、epoll等,但是libevent使用结构体eventop进行了封装,以统一的接口来支持这些I/O多路复用机制,达到了对外隐藏底层系统机制的目的。3)Reactor——反应器Reactor,是事件管理的接口,内部使用eventdemultiplexer注册、注销事件;并运行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数处理事件。对应到libevent中,就是event_base结构体。一个典型的Reactor声明方式viewplaincopytoclipboardprint?classReactor{public:intregister_handler(Event_Handler*pHandler,intevent);intremove_handler(Event_Handler*pHandler,intevent);voidhandle_events(timeval*ptv);//...};classReactor{public:intregister_handler(Event_Handler*pHandler,intevent);intremove_handler(Event_Handler*pHandler,intevent);voidhandle_events(timeval*ptv);//...};4)EventHandler——事件处理程序事件处理程序提供了一组接口,每个接口对应了一种类型的事件,供Reactor在相应的事件发生时调用,执行相应的事件处理。通常它会绑定一个有效的句柄。对应到libevent中,就是event结构体。下面是两种典型的EventHandler类声明方式,二者互有优缺点。viewplaincopytoclipboardprint?classEvent_Handler{public:virtualvoidhandle_read()=0;virtualvoidhandle_write()=0;virtualvoidhandle_timeout()=0;virtualvoidhandle_close()=0;virtualHANDLEget_handle()=0;//...};classEvent_Handler{public://eventsmayberead/write/timeout/close.etcvirtualvoidhandle_events(intevents)=0;virtualHANDLEget_handle()=0;//...};classEvent_Handler{public:virtualvoidhandle_read()=0;virtualvoidhandle_write()=0;virtualvoidhandle_timeout()=0;virtualvoidhandle_close()=0;virtualHANDLEget_handle()=0;//...};classEvent_Handler{public://eventsmayberead/write/timeout/close.etcvirtualvoidhandle_events(intevents)=0;virtualHANDLEget_handle()=0;//...};4Reactor事件处理流程前面说过Reactor将事件流“逆置”了,那么使用Reactor模式后,事件控制流是什么样子呢?可以参见下面的序列图。5小结上面讲到了Reactor的基本概念、框架和处理流程,对Reactor有个基本清晰的了解后,再来对比看libevent就会更容易理解了,接下来就正式进入到libevent的代码世界了,加油!参考资料:Pattern-OrientedSoftwareArchitecture,PatternsforConcurrentandNetworkedObjects,Volume2libevent基本使用场景和事件流程张亮1前言学习源代码该从哪里入手?我觉得从程序的基本使用场景和代码的整体处理流程入手是个不错的方法,至少从个人的经验上讲,用此方法分析libevent是比较有效的。2基本应用场景基本应用场景也是使用libevnet的基本流程,下面来考虑一个最简单的场景,使用livevent设置定时器,应用程序只需要执行下面几个简单的步骤即可。1)首先初始化libevent库,并保存返回的指针structevent_base*base=event_init();实际上这一步相当于初始化一个Reactor实例;在初始化libevent后,就可以注册事件了。2)初始化事件event,设置回调函数和关注的事件evtimer_set(&ev,timer_cb,NULL);事实上这等价于调用event_set(&ev,-1,0,timer_cb,NULL);event_set的函数原型是:voidevent_set(structevent*ev,intfd,shortevent,void(*cb)(int,short,void*),void*arg)ev:执行要初始化的event对象;fd:该event绑定的“句柄”,对于信号事件,它就是关注的信号;event:在该fd上关注的事件类型,它可以是EV_READ,EV_WRITE,EV_SIGNAL;cb:这是一个函数指针,当fd上的事件event发生时,调用该函数执行处理,它有三个参数,调用时由event_base负责传入,按顺序,实际上就是event_set时的fd,event和arg;arg:传递给cb函数指针的参数;由于定时事件不需要fd,并且定时事件是根据添加时(event_add)的超时值设定的,因此这里event也不需要设置。这一步相当于初始化一个eventhandler,在libevent中事件类型保存在event结构体中。注意:libevent并不会管理event事件集合,这需要应用程序自行管理;3)设置event从属的event_baseevent_base_set(base,&ev);这一步相当于指明event要注册到哪个event_base实例上;4)是正式的添加事件的时候了event_add(&ev,timeout);基本信息都已设置完成,只要简单的调用event_add()函数即可完成,其中timeout是定时值;这一步相当于调用Reactor::register_handler()函数注册事件。5)程序进入无限循环,等待就绪事件并执行事件处理event_base_dispatch(base);3实例代码上面例子的程序代码如下所示structeventev;structtimevaltv;voidtime_cb(intfd,shortevent,void*argc){printf(timerwakeup\n);event_add(&ev,&tv);//rescheduletimer}intmain(){structeve
本文标题:libevent全面源码探究
链接地址:https://www.777doc.com/doc-3403224 .html