ImageVerifierCode 换一换
格式:DOCX , 页数:45 ,大小:239.84KB ,
资源ID:11123488      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/11123488.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(libevent源码深度剖析.docx)为本站会员(b****8)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

libevent源码深度剖析.docx

1、libevent源码深度剖析libevent源码深度剖析一*序幕1 前言Libevent是一个轻量级的开源高性能网络库,使用者众多,研究者更甚,相关文章也不少。写这一系列文章的用意在于,一则分享心得;二则对libevent代码和设计思想做系统的、更深层次的分析,写出来,也可供后来者参考。附带一句:Libevent是用c语言编写的(MS大牛们都偏爱c语言哪),而且几乎是无处不函数指针,学习其源代码也需要相当的c语言基础。2 Libevent简介上来当然要先夸奖啦,Libevent 有几个显著的亮点:事件驱动(event-driven),高性能;轻量级,专注于网络,不如ACE那么臃肿庞大;源代码相

2、当精炼、易读;跨平台,支持Windows、Linux、*BSD和Mac Os;支持多种I/O多路复用技术, epoll、poll、dev/poll、select和kqueue等;支持I/O,定时器和信号等事件;注册事件优先级;Libevent已经被广泛的应用,作为底层的网络库;比如memcached、Vomit、Nylon、Netchat等等。Libevent当前的最新稳定版是1.4.13;这也是本文参照的版本。3 学习的好处学习libevent有助于提升程序设计功力,除了网络程序设计方面外,Libevent的代码里有很多有用的设计技巧和基础数据结构,比如信息隐藏、函数指针、c语言的多态支持、

3、链表和堆等等,都有助于提升自身的程序功力。程序设计不止要了解框架,很多细节之处恰恰也是事关整个系统成败的关键。只对libevent本身的框架大概了解,那或许仅仅是一知半解,不深入代码分析,就难以了解其设计的精巧之处,也就难以为自己所用。事实上Libevent本身就是一个典型的Reactor模型,理解Reactor模式是理解libevent的基石;因此下一节将介绍典型的事件驱动设计模式Reactor模式。libevent源码深度剖析二Reactor模式张亮前面讲到,整个libevent本身就是一个Reactor,因此本节将专门对Reactor模式进行必要的介绍,并列出libevnet中的几个重要

4、组件和Reactor的对应关系,在后面的章节中可能还会提到本节介绍的基本概念。1 Reactor的事件处理机制首先来回想一下普通函数调用的机制:程序调用某函数?函数执行,程序等待?函数将结果和控制权返回给程序?程序继续处理。Reactor释义“反应堆”,是一种事件驱动机制。和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。使用Libevent也是想Libevent框架注册相应的事件和

5、回调函数;当这些时间发声时,Libevent会调用这些回调函数处理相应的事件(I/O读写、定时和信号)。 用“好莱坞原则”来形容Reactor再合适不过了:不要打电话给我们,我们会打电话通知你。 举个例子:你去应聘某xx公司,面试结束后。“普通函数调用机制”公司HR比较懒,不会记你的联系方式,那怎么办呢,你只能面试完后自己打电话去问结果;有没有被录取啊,还是被据了;“Reactor”公司HR就记下了你的联系方式,结果出来后会主动打电话通知你:有没有被录取啊,还是被据了;你不用自己打电话去问结果,事实上也不能,你没有HR的留联系方式。2 Reactor模式的优点Reactor模式是编写高性能网络

6、服务器的必备技术之一,它具有如下的优点: 1)响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的; 2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销; 3)可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源; 4)可复用性,reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;3 Reactor模式框架 使用Reactor模型,必备的几个组件:事件源、Reactor框架、多路复用机制和事件处理程序,先来看看Reactor模型的整体框架,接下来再对每个组件做逐一说明。1) 事件源Linux上是文件描述符,

7、Windows上就是Socket或者Handle了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,比如I/O事件。2) event demultiplexer事件多路分发机制由操作系统提供的I/O多路复用机制,比如select和epoll。 程序首先将其关心的句柄(事件源)及其事件注册到event demultiplexer上;当有事件到达时,event demultiplexer会发出通知“在已经注册的句柄集中,一个或多个句柄的事件已经就绪”; 程序收到通知后,就可以在非阻塞的情况下对事件进行处理了。对应到libevent中,依然是select、poll、epoll等,但是lib

8、event使用结构体eventop进行了封装,以统一的接口来支持这些I/O多路复用机制,达到了对外隐藏底层系统机制的目的。3) Reactor反应器 Reactor,是事件管理的接口,内部使用event demultiplexer注册、注销事件;并运行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数处理事件。对应到libevent中,就是event_base结构体。一个典型的Reactor声明方式view plaincopy to clipboardprint?1.classReactor2.3.public:4.intregister_handler(Event_Handler*

9、pHandler,intevent);5.intremove_handler(Event_Handler*pHandler,intevent);6.voidhandle_events(timeval*ptv);7./.8.;4) Event Handler事件处理程序 事件处理程序提供了一组接口,每个接口对应了一种类型的事件,供Reactor在相应的事件发生时调用,执行相应的事件处理。通常它会绑定一个有效的句柄。对应到libevent中,就是event结构体。下面是两种典型的Event Handler类声明方式,二者互有优缺点。view plaincopy to clipboardprint?

10、1.classEvent_Handler2.3.public:4.virtualvoidhandle_read()=0;5.virtualvoidhandle_write()=0;6.virtualvoidhandle_timeout()=0;7.virtualvoidhandle_close()=0;8.virtualHANDLEget_handle()=0;9./.10.;11.classEvent_Handler12.13.public:14./eventsmayberead/write/timeout/close.etc15.virtualvoidhandle_events(intev

11、ents)=0;16.virtualHANDLEget_handle()=0;17./.18.;4 Reactor事件处理流程前面说过Reactor将事件流“逆置”了,那么使用Reactor模式后,事件控制流是什么样子呢?可以参见下面的序列图。5 小结上面讲到了Reactor的基本概念、框架和处理流程,对Reactor有个基本清晰的了解后,再来对比看libevent就会更容易理解了,接下来就正式进入到libevent的代码世界了,加油!libevent源码深度剖析三libevent基本使用场景和事件流程张亮1 前言学习源代码该从哪里入手?我觉得从程序的基本使用场景和代码的整体处理流程入手是个不

12、错的方法,至少从个人的经验上讲,用此方法分析libevent是比较有效的。2 基本应用场景基本应用场景也是使用libevnet的基本流程,下面来考虑一个最简单的场景,使用livevent设置定时器,应用程序只需要执行下面几个简单的步骤即可。1)首先初始化libevent库,并保存返回的指针struct event_base * base = event_init();实际上这一步相当于初始化一个Reactor实例;在初始化libevent后,就可以注册事件了。2)初始化事件event,设置回调函数和关注的事件evtimer_set(&ev, timer_cb, NULL);事实上这等价于调用e

13、vent_set(&ev, -1, 0, timer_cb, NULL);event_set的函数原型是:void event_set(struct event *ev, int fd, short event, void (*cb)(int, short, void *), void *arg)ev:执行要初始化的event对象;fd:该event绑定的“句柄”,对于信号事件,它就是关注的信号;event:在该fd上关注的事件类型,它可以是EV_READ, EV_WRITE, EV_SIGNAL;cb:这是一个函数指针,当fd上的事件event发生时,调用该函数执行处理,它有三个参数,调用时

14、由event_base负责传入,按顺序,实际上就是event_set时的fd, event和arg;arg:传递给cb函数指针的参数;由于定时事件不需要fd,并且定时事件是根据添加时(event_add)的超时值设定的,因此这里event也不需要设置。这一步相当于初始化一个event handler,在libevent中事件类型保存在event结构体中。注意:libevent并不会管理event事件集合,这需要应用程序自行管理;3)设置event从属的event_baseevent_base_set(base, &ev);这一步相当于指明event要注册到哪个event_base实例上;4)是

15、正式的添加事件的时候了event_add(&ev, timeout);基本信息都已设置完成,只要简单的调用event_add()函数即可完成,其中timeout是定时值;这一步相当于调用Reactor:register_handler()函数注册事件。5)程序进入无限循环,等待就绪事件并执行事件处理event_base_dispatch(base);3 实例代码上面例子的程序代码如下所示view plaincopy to clipboardprint?1.structeventev;2.structtimevaltv;3.voidtime_cb(intfd,shortevent,void*ar

16、gc)4.5.printf(timerwakeupn);6.event_add(&ev,&tv);/rescheduletimer7.8.intmain()9.10.structevent_base*base=event_init();11.tv.tv_sec=10;/10speriod12.tv.tv_usec=0;13.evtimer_set(&ev,time_cb,NULL);14.event_add(&ev,&tv);15.event_base_dispatch(base);16.4 事件处理流程当应用程序向libevent注册一个事件后,libevent内部是怎么样进行处理的呢?下面

17、的图就给出了这一基本流程。1) 首先应用程序准备并初始化event,设置好事件类型和回调函数;这对应于前面第步骤2和3;2) 向libevent添加该事件event。对于定时事件,libevent使用一个小根堆管理,key为超时时间;对于Signal和I/O事件,libevent将其放入到等待链表(wait list)中,这是一个双向链表结构;3) 程序调用event_base_dispatch()系列函数进入无限循环,等待事件,以select()函数为例;每次循环前libevent会检查定时事件的最小超时时间tv,根据tv设置select()的最大等待时间,以便于后面及时处理超时事件;当se

18、lect()返回后,首先检查超时事件,然后检查I/O事件;Libevent将所有的就绪事件,放入到激活链表中;然后对激活链表中的事件,调用事件的回调函数执行事件处理;5 小结本节介绍了libevent的简单实用场景,并旋风般的介绍了libevent的事件处理流程,读者应该对libevent有了基本的印象,下面将会详细介绍libevent的事件管理框架(Reactor模式中的Reactor框架)做详细的介绍,在此之前会对源代码文件做简单的分类。libevent源码深度剖析四libevent源代码文件组织1 前言详细分析源代码之前,如果能对其代码文件的基本结构有个大概的认识和分类,对于代码的分析将

19、是大有裨益的。本节内容不多,我想并不是说它不重要!2 源代码组织结构Libevent的源代码虽然都在一层文件夹下面,但是其代码分类还是相当清晰的,主要可分为头文件、内部使用的头文件、辅助功能函数、日志、libevent框架、对系统I/O多路复用机制的封装、信号管理、定时事件管理、缓冲区管理、基本数据结构和基于libevent的两个实用库等几个部分,有些部分可能就是一个源文件。源代码中的test部分就不在我们关注的范畴了。1)头文件主要就是event.h:事件宏定义、接口函数声明,主要结构体event的声明;2)内部头文件xxx-internal.h:内部数据结构和函数,对外不可见,以达到信息隐

20、藏的目的;3)libevent框架event.c:event整体框架的代码实现;4)对系统I/O多路复用机制的封装epoll.c:对epoll的封装;select.c:对select的封装;devpoll.c:对dev/poll的封装;kqueue.c:对kqueue的封装;5)定时事件管理min-heap.h:其实就是一个以时间作为key的小根堆结构;6)信号管理signal.c:对信号事件的处理;7)辅助功能函数evutil.h 和evutil.c:一些辅助功能函数,包括创建socket pair和一些时间操作函数:加、减和比较等。8)日志log.h和log.c:log日志函数9)缓冲区管

21、理evbuffer.c和buffer.c:libevent对缓冲区的封装;10)基本数据结构compatsys下的两个源文件:queue.h是libevent基本数据结构的实现,包括链表,双向链表,队列等;_libevent_time.h:一些用于时间操作的结构体定义、函数和宏定义;11)实用网络库http和evdns:是基于libevent实现的http服务器和异步dns查询库;3 小结本节介绍了libevent的组织和分类,下面将会详细介绍libevent的核心部分event结构。libevent源码深度剖析五libevent的核心:事件event张亮对事件处理流程有了高层的认识后,本节将

22、详细介绍libevent的核心结构event,以及libevent对event的管理。1 libevent的核心-event Libevent是基于事件驱动(event-driven)的,从名字也可以看到event是整个库的核心。event就是Reactor框架中的事件处理程序组件;它提供了函数接口,供Reactor在事件发生时调用,以执行相应的事件处理,通常它会绑定一个有效的句柄。首先给出event结构体的声明,它位于event.h文件中:view plaincopy to clipboardprint?1.structevent2.TAILQ_ENTRY(event)ev_next;3.T

23、AILQ_ENTRY(event)ev_active_next;4.TAILQ_ENTRY(event)ev_signal_next;5.unsignedintmin_heap_idx;/*formanagingtimeouts*/6.structevent_base*ev_base;7.intev_fd;8.shortev_events;9.shortev_ncalls;10.short*ev_pncalls;/*Allowsdeletesincallback*/11.structtimevalev_timeout;12.intev_pri;/*smallernumbersarehigher

24、priority*/13.void(*ev_callback)(int,short,void*arg);14.void*ev_arg;15.intev_res;/*resultpassedtoeventcallback*/16.intev_flags;17.;下面简单解释一下结构体中各字段的含义。1)ev_events:event关注的事件类型,它可以是以下3种类型:I/O事件: EV_WRITE和EV_READ定时事件:EV_TIMEOUT信号: EV_SIGNAL辅助选项:EV_PERSIST,表明是一个永久事件Libevent中的定义为:view plaincopy to clipboa

25、rdprint?1.#defineEV_TIMEOUT0x012.#defineEV_READ0x023.#defineEV_WRITE0x044.#defineEV_SIGNAL0x085.#defineEV_PERSIST0x10/*Persistantevent*/可以看出事件类型可以使用“|”运算符进行组合,需要说明的是,信号和I/O事件不能同时设置;还可以看出libevent使用event结构体将这3种事件的处理统一起来;2)ev_next,ev_active_next和ev_signal_next都是双向链表节点指针;它们是libevent对不同事件类型和在不同的时期,对事件的管理

26、时使用到的字段。libevent使用双向链表保存所有注册的I/O和Signal事件,ev_next就是该I/O事件在链表中的位置;称此链表为“已注册事件链表”;同样ev_signal_next就是signal事件在signal事件链表中的位置;ev_active_next:libevent将所有的激活事件放入到链表active list中,然后遍历active list执行调度,ev_active_next就指明了event在active list中的位置;2)min_heap_idx和ev_timeout,如果是timeout事件,它们是event在小根堆中的索引和超时值,libevent使

27、用小根堆来管理定时事件,这将在后面定时事件处理时专门讲解3)ev_base该事件所属的反应堆实例,这是一个event_base结构体,下一节将会详细讲解;4)ev_fd,对于I/O事件,是绑定的文件描述符;对于signal事件,是绑定的信号;5)ev_callback,event的回调函数,被ev_base调用,执行事件处理程序,这是一个函数指针,原型为:void (*ev_callback)(int fd, short events, void *arg)其中参数fd对应于ev_fd;events对应于ev_events;arg对应于ev_arg;6)ev_arg:void*,表明可以是任意

28、类型的数据,在设置event时指定;7)eb_flags:libevent用于标记event信息的字段,表明其当前的状态,可能的值有:view plaincopy to clipboardprint?1.#defineEVLIST_TIMEOUT0x01/event在time堆中2.#defineEVLIST_INSERTED0x02/event在已注册事件链表中3.#defineEVLIST_SIGNAL0x04/未见使用4.#defineEVLIST_ACTIVE0x08/event在激活链表中5.#defineEVLIST_INTERNAL0x10/内部使用标记6.#defineEVLI

29、ST_INIT0x80/event已被初始化8)ev_ncalls:事件就绪执行时,调用ev_callback的次数,通常为1;9)ev_pncalls:指针,通常指向ev_ncalls或者为NULL;10)ev_res:记录了当前激活事件的类型;2 libevent对event的管理 从event结构体中的3个链表节点指针和一个堆索引出发,大体上也能窥出libevent对event的管理方法了,可以参见下面的示意图: 每次当有事件event转变为就绪状态时,libevent就会把它移入到active event listpriority中,其中priority是event的优先级;接着libevent会根据自己的调度策略选择就绪事件,调用其cb_callback()函数执行事件处理;并根据就绪的句柄和事件类型填充cb_callback函数的参数。3 事件设置的接口函数 要向libevent添加一个事件,需要首先设置event对象,这通过调用libevent提供的函数有:event_set(), event_base_set(), event_priority_set()来完成;下面分别进行讲解。void event_set(struct even

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1