libevent源码深度剖析
- 格式:doc
- 大小:423.00 KB
- 文档页数:39
libevent源码深度剖析十三libevent信号处理注意点系列目录(1)libevent源码深度剖析一序(2)libevent源码深度剖析二 Reactor模式(3)libevent源码深度剖析三 libevent基本使用场景和事件流程(4)libevent源码深度剖析四 libevent源代码文件组织(5)libevent源码深度剖析五 libevent的核心:事件event(6)libevent源码深度剖析六初见事件处理框架(7)libevent源码深度剖析七事件主循环(8)libevent源码深度剖析八集成信号处理(9)libevent源码深度剖析九集成定时器事件(10)libevent源码深度剖析十支持I/O多路复用技术(11)libevent源码深度剖析十一时间管理(12)libevent源码深度剖析十二让libevent支持多线程(13)libevent源码深度剖析十三 libevent信号处理注意点前面讲到了 libevent 实现多线程的方法,然而在多线程的环境中注册信号事件,还是有一些情况需要小心处理,那就是不能在多个 libevent 实例上注册信号事件。
依然冠名追加到 libevent 系列。
以 2 个线程为例,做简单的场景分析。
1 首先是创建并初始化线程 1 的 libevent 实例 base1 ,线程 1 的 libevent 实例 base2 ;2 在 base1 上注册 SIGALRM 信号;在 base2 上注册 SIGINT 信号;3 假设当前 base1 和 base2 上都没有注册其他的事件;4 线程 1 和 2 都进入 event_base_loop 事件循环:5 假设线程 1 先进入 event_base_loop ,并设置 evsignal_base = base1 ;并等待;6 接着线程 2 也进入 event_base_loop ,并设置 evsignal_base = base2 ;并等待;于是 evsignal_base 就指向了 base2 ;7 信号 ALARM 触发,调用服务例程:1static void evsignal_handler(int sig){2 ...3 evsignal_base->sig.evsigcaught[sig]++;4 evsignal_base->sig.evsignal_caught = 1;5 /* Wake up our notification mechanism */6 send(evsignal_base->sig.ev_signal_pair[0], 'a', 1, 0);7 ...8}于是 base2 得到通知 ALARM 信号发生了,而实际上 ALARM 是注册在 base1 上的, base2 上的 ALARM 注册 event 是空的,于是处理函数将不能得到调用;因此在 libevent 中,如果需要处理信号,只能将信号注册到一个 libevent 实例上。
libevent源码分析这两天没事,看了⼀下Memcached和libevent的源码,做个⼩总结。
1、⼊门1.1、概述Libevent是⼀个⽤于开发可扩展性⽹络服务器的基于事件驱动(event-driven)模型的⽹络库。
Libevent有⼏个显著的亮点:(1)事件驱动(event-driven),⾼性能;(2)轻量级,专注于⽹络,不如 ACE 那么臃肿庞⼤;(3)源代码相当精炼、易读;(4)跨平台,⽀持 Windows、Linux、*BSD和 Mac Os;(5)⽀持多种 I/O多路复⽤技术, epoll、poll、dev/poll、select 和kqueue 等;(6)⽀持 I/O,定时器和信号等事件;(7)注册事件优先级;Libevent 已经被⼴泛的应⽤,作为底层的⽹络库;⽐如 memcached、 Vomi t、 Nylon、 Netchat等等。
1.2、⼀个简单⽰例代码1int lasttime;23static void4 timeout_cb(int fd, short event, void *arg)5 {6struct timeval tv;7struct event *timeout = arg;8int newtime = time(NULL);910//printf("%s: called at %d: %d\n", __func__, newtime,11 printf("%s: called at %d: %d\n", "timeout_cb", newtime,12 newtime - lasttime);13 lasttime = newtime;1415 evutil_timerclear(&tv);16 _sec = 2;17//重新注册event18 event_add(timeout, &tv);19 }2021int22 main (int argc, char **argv)23 {24struct event timeout;25struct timeval tv;2627/* Initalize the event library */28//初始化event环境29 event_init();3031/* Initalize one event */32//设置事件33 evtimer_set(&timeout, timeout_cb, &timeout);3435 evutil_timerclear(&tv);36 _sec = 2;37//注册事件38 event_add(&timeout, &tv);3940 lasttime = time(NULL);4143 event_dispatch();4445return (0);46 }这是⼀个简单的基于libevent的定时器程序,运⾏结果:⽤libevent编程⾮常简单,只需要调⽤event_init初始化环境,然后调⽤event_add注册相应的事件,接着调⽤event_dispatch等待并处理相应的事件即可。
最近项目赶着发版本,连续好几天通宵赶工,瓶颈出现在了性能问题,各种方案验证和修订。
周末闲下来还是对之前学习的libevent进行总结。
在libevent的报文收发处理过程中采用了一系列提高收发性能的技术,其中多缓冲区的接收和发送以及零拷贝技术等,本篇主要分析这些技术在libevent中的运用。
首先简要的介绍一下两种技术:多缓存的收据发送和接收:点击(此处)折叠或打开1.struct iovec {2. ptr_t iov_base;/* Starting address */3. size_t iov_len;/* Length in bytes */4.};5.6.int readv(int fd,const struct iovec *vector,int count);7.int writev(int fd,const struct iovec *vector,int count);其中的iovec是指一个缓冲区,包含了数据区的地址和对应的长度,在两个函数中的count是指iovec的个数。
这种多缓冲区的发送和接收操作性能相对更好。
而iovec需要在使用前分配好相关的内存空间。
零拷贝技术:零拷贝能够减少数据之间无效的数据拷贝,而直接进行数据的发送,通常在数据发送的过程中使用,特别是在文件发送的过程中被经常使用。
通常情况下要将文件a.txt中的内容发送出去,需要进行如下的操作:读取文件内容: read(),然后发送读取的内容send()。
因此一个完整的过程会出现一个读取再发送的操作,往往文件的IO操作是相对费时的操作,因此零拷贝技术实际上就是较少了read()的处理过程,即在发送数据前不需要进行文件的读取操作,这样相对而言就会提高处理的性能。
关于零拷贝的技术有很多方式,这里主要介绍sendfile和mmap.其中的mmap是采用映射的方式将文件内容映射到内存中,在发送报文时直接读取内存中的内容,这样就能提高发送效率。
libevent源码分析event_base_dispatch,event_base_lo。
接⼝:event_base_dispatch/**Event dispatching loop 事件分配循环This loop will run the event base until either there are no more pending oractive, or until something calls event_base_loopbreak() orevent_base_loopexit().这个循环将会运⾏event base,知道没有等待的或者活动的事件,或者其它的调⽤了event_base_loopbreak()或event_base_loopexit().@param base the event_base structure returned by event_base_new() orevent_base_new_with_config() event_base_new() 或者 event_base_new_with_config() 返回的event_base对象@return 0 if successful, -1 if an error occurred, or 1 if we exited becauseno events were pending or active. 成功返回0,错误返回-1,或者1(当没有等待的或者活动事件时退出,会返回1)@see event_base_loop()*/EVENT2_EXPORT_SYMBOLint event_base_dispatch(struct event_base *base);intevent_base_dispatch(struct event_base *event_base){return (event_base_loop(event_base, 0));}跟踪event_base_loop接⼝/**Wait for events to become active, and run their callbacks.等待events 变成活动的,并运⾏对应的回调函数。
libevent编译
LibEVENT是一款跨平台的基于BSD下的高性能异步事件驱动库,用于解决异步编程难题。
它能够有效地执行异步I/O或时间事件,可极大提高程序的性能,使之变得优雅。
libevent既可以单独使用,也可以与应用程序(比如web服务器)进行集成,可是以模块的方式实现,做到最大的性能。
LibEvent的编译方法简单易行,有基于Linux、Windows、Mac、BSD等不同操作系统的编译工具。
编译分为configure-make-install三步:
首先,确定libevent的源码包路径,使用linux shell命令进行当前路径切换,cmd进行切换;
其次,执行configure脚本,这是一个自动构建libevent库所需的步骤,命令参数格式如./configure,此步负责生成makefile文件;
最后,执行make命令,此步负责根据makefile文件来编译库文件,编译完成后,安装对应的执行文件。
LibEvent的编译相对来说容易,但需要搭配系统环境,无法满足针对每一种操作系统进行定制。
因此,有时候我们可能需要重新编译libevent,以满足自己应用系统的需求,才能达到最佳性能。
libev事件库详解(⼆)事件库之Libev(⼀)使⽤Libev#include<ev.h>#include <stdio.h>#include <signal.h>#include <sys/unistd.h>ev_io io_w;ev_timer timer_w;ev_signal signal_w;void io_action(struct ev_loop *main_loop,ev_io *io_w,int e){int rst;char buf[1024] = {'\0'};puts("in io cb\n");read(STDIN_FILENO,buf,sizeof(buf));buf[1023] = '\0';printf("Read in a string %s \n",buf);ev_io_stop(main_loop,io_w);}void timer_action(struct ev_loop *main_loop,ev_timer *timer_w,int e){puts("in tiemr cb \n");ev_timer_stop(main_loop,timer_w);}void signal_action(struct ev_loop *main_loop,ev_signal signal_w,int e){puts("in signal cb \n");ev_signal_stop(main_loop,signal_w);ev_break(main_loop,EVBREAK_ALL);}int main(int argc ,char *argv[]){struct ev_loop *main_loop = ev_default_loop(0);ev_init(&io_w,io_action);ev_io_set(&io_w,STDIN_FILENO,EV_READ);ev_init(&timer_w,timer_action);ev_timer_set(&timer_w,2,0);ev_init(&signal_w,signal_action);ev_signal_set(&signal_w,SIGINT);ev_io_start(main_loop,&io_w);ev_timer_start(main_loop,&timer_w);ev_signal_start(main_loop,&signal_w);ev_run(main_loop,0);return0;}下⾯对使⽤到的这些API进⾏说明。
Libevent学习——事件主循环8.1、Libevent的事件主循环主要是通过event_base_loop ()函数完成的,其主要操作如下⾯的流程图所⽰,event_base_loop 所作的就是持续执⾏下⾯的循环。
来看下函数源码:intevent_base_loop(struct event_base *base, int flags){const struct eventop *evsel = base->evsel;//多路复⽤IOstruct timeval tv;struct timeval *tv_p;int res, done, retval = 0;EVBASE_ACQUIRE_LOCK(base, th_base_lock);if (base->running_loop) {//检测event_base_loop是否已经运⾏,每个event_base只有⼀个event_base_loopevent_warnx("%s: reentrant invocation. Only one event_base_loop"" can run on each event_base at once.", __func__);EVBASE_RELEASE_LOCK(base, th_base_lock);return -1;}base->running_loop = 1;clear_time_cache(base);//清空时间缓存if (base->sig.ev_signal_added && base->sig.ev_n_signals_added)evsig_set_base(base);// evsignal_base是全局变量,在处理signal时,⽤于指名signal所属的event_base实例done = 0;#ifndef _EVENT_DISABLE_THREAD_SUPPORTbase->th_owner_id = EVTHREAD_GET_ID();#endifbase->event_gotterm = base->event_break = 0;while (!done) {// 事件主循环base->event_continue = 0;if (base->event_gotterm) {break;// 查看是否需要跳出循环,程序可以调⽤event_loopexit_cb()设置event_gotterm标记}if (base->event_break) {break;// 调⽤event_base_loopbreak()设置event_break标记}// 校正系统时间,如果系统使⽤的是⾮MONOTONIC时间,⽤户可能会向后调整了系统时间// 在timeout_correct函数⾥,⽐较last wait time和当前时间,如果当前时间< last wait time// 表明时间有问题,这是需要更新timer_heap中所有定时事件的超时时间。
libevent源码深度剖析第⼀章1,前⾔Libevent是⼀个轻量级的开源⾼性能⽹络库,使⽤者众多,研究者更甚,相关⽂章也不少。
写这⼀系列⽂章的⽤意在于,⼀则分享⼼得;⼆则对libevent代码和设计思想做系统的、更深层次的分析,写出来,也可供后来者参考。
附带⼀句:Libevent是⽤c语⾔编写的(MS⼤⽜们都偏爱c语⾔哪),⽽且⼏乎是⽆处不函数指针,学习其源代码也需要相当的c语⾔基础。
2,libevent简介上来当然要先夸奖啦,Libevent 有⼏个显著的亮点:=> 事件驱动(event-driven),⾼性能;=> 轻量级,专注于⽹络,不如ACE那么臃肿庞⼤;=> 源代码相当精炼、易读;=> 跨平台,⽀持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语⾔的多态⽀持、链表和堆等等,都有助于提升⾃⾝的程序功⼒。
程序设计不⽌要了解框架,很多细节之处恰恰也是事关整个系统成败的关键。
只对libevent本⾝的框架⼤概了解,那或许仅仅是⼀知半解,不深⼊代码分析,就难以了解其设计的精巧之处,也就难以为⾃⼰所⽤。
事实上Libevent本⾝就是⼀个典型的Reactor模型,理解Reactor模式是理解libevent的基⽯;因此下⼀节将介绍典型的事件驱动设计模式——Reactor模式。
libevent源码深度剖析张亮Email: sparling.liang@回想刚开始写时,就冠以“深度剖析”的名称,也是为了给自己一些压力,以期能写好这一系列文章,对libevent源代码的各方面作详细的分析;现在看来也算是达到了最初的目的。
希望能给学习和使用libevent的朋友们有所帮助。
Email:sparkling.liang@张亮目录libevent源码深度剖析 (1)目录 (3)一序幕 (5)1 前言 (5)2 Libevent简介 (5)3 学习的好处 (5)二Reactor模式 (6)1 Reactor的事件处理机制 (6)2 Reactor模式的优点 (6)3 Reactor模式框架 (6)4 Reactor事件处理流程 (8)5 小结 (9)三基本使用场景和事件流程 (10)1 前言 (10)2 基本应用场景 (10)3 实例代码 (11)4 事件处理流程 (11)5 小结 (12)四 libevent源代码文件组织 (13)1 前言 (13)2 源代码组织结构 (13)3 小结 (14)五 libevent的核心:事件event (15)1 libevent的核心-event (15)2 libevent对event的管理 (16)3 事件设置的接口函数 (17)4 小结 (18)六初见事件处理框架 (19)1 事件处理框架-event_base (19)2 创建和初始化event_base (20)3 接口函数 (20)4 小节 (23)七事件主循环 (24)1 阶段性的胜利 (24)2 事件处理主循环 (24)3 I/O和Timer事件的统一 (27)4 I/O和Signal事件的统一 (27)5 小节 (27)八集成信号处理 (28)1 集成策略——使用socket pair (28)2 集成到事件主循环——通知event_base (29)4 evsignal_info结构体 (30)5 注册、注销signal事件 (30)5 小节 (31)九集成定时器事件 (32)1 集成到事件主循环 (32)2 Timer小根堆 (33)3 小节 (34)十支持I/O多路复用技术 (35)1 统一的关键 (35)2 设置I/O demultiplex机制 (35)3 小节 (37)十一时间管理 (38)1 初始化检测 (38)2 时间缓存 (38)3 时间校正 (40)4 小节 (41)十二让libevent支持多线程 (42)1 错误使用示例 (42)2 支持多线程的几种模式 (42)3 例子——memcached (43)4 小节 (44)一序幕1 前言Libevent是一个轻量级的开源高性能网络库,使用者众多,研究者更甚,相关文章也不少。
libev源码解析⼀ libev简介 libev是⼀个轻量级的事件通知库,具备⽀持多种事件通知能⼒,通过对libev的源码的阅读,可以清楚了解事件通知实现内部机制。
⼆核⼼数据结构在libev中关键的数据结构是,loop结构体,该结构体定义的字段较多,但是主要核⼼的可以分为两⼤类ev_loop结构体(loop为ev_loop结构的全局变量)的字段定义在ev_vars.h头⽂件中,然后在ev.c中通过include的⽅式导⼊1.各类事件的watcher集合 loop中有⽀持很多类型的事件,如下ev_io // IO可读可写ev_stat // ⽂件属性变化ev_signal // 信号处理ev_timer // 相对定时器ev_periodic // 绝对定时器ev_child // ⼦进程状态变化ev_fork // fork事件ev_cleanup // event loop退出触发事件ev_idle // event loop空闲触发事件ev_embed // 嵌⼊另⼀个后台循环ev_prepare // event loop之前事件ev_check // event loop之后事件ev_async // 线程间异步事件这些事件的监控管理都对应⼀种类型的watcher数组,⽐如(loop)->anfds :维护所有fd事件(loop)->timers :维护所有的定时器(loop)->periodics:周期性事件(loop)->prepares:该事件是loop启动之间就会执⾏的事件等,每类事件都能在loop结构中找到对应的数组来维护对应的watchers。
2.2 watcher结构对于不同类型的事件的watcher,采⽤继承的⽅式来实现各个类型的watcher,libev是使⽤c的宏定义来实现继承(宏的奇技淫巧在libev中随处可见,这也导致libev看起来⽐较晦涩)这个是公共watcher得到结构,是会被所有的⼦watcher所共享。
libevent源码深度剖析张亮1 前言Libevent是一个轻量级的开源高性能网络库,使用者众多,研究者更甚,相关文章也不少。
写这一系列文章的用意在于,一则分享心得;二则对libevent代码和设计思想做系统的、更深层次的分析,写出来,也可供后来者参考。
附带一句:Libevent是用c语言编写的(MS大牛们都偏爱c语言哪),而且几乎是无处不函数指针,学习其源代码也需要相当的c语言基础。
Libevent简介上来当然要先夸奖啦,Libevent 有几个显著的亮点:事件驱动(event-driven),高性能;轻量级,专注于网络,不如ACE那么臃肿庞大;源代码相当精炼、易读;跨平台,支持Windows、Linux、*BSD和Mac Os;支持多种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的对应关系,在后面的章节中可能还会提到本节介绍的基本概念。
1 Reactor的事件处理机制首先来回想一下普通函数调用的机制:程序调用某函数?函数执行,程序等待?函数将结果和控制权返回给程序?程序继续处理。
Reactor释义“反应堆”,是一种事件驱动机制。
和普通函数调用的不同之处在于:应用程序不是主动的调用某个API完成处理,而是恰恰相反,Reactor逆置了事件处理流程,应用程序需要提供相应的接口并注册到Reactor上,如果相应的时间发生,Reactor将主动调用应用程序注册的接口,这些接口又称为“回调函数”。
使用Libevent也是想Libevent框架注册相应的事件和回调函数;当这些时间发声时,Libevent会调用这些回调函数处理相应的事件(I/O 读写、定时和信号)。
用“好莱坞原则”来形容Reactor再合适不过了:不要打电话给我们,我们会打电话通知你。
举个例子:你去应聘某xx公司,面试结束后。
“普通函数调用机制”公司HR比较懒,不会记你的联系方式,那怎么办呢,你只能面试完后自己打电话去问结果;有没有被录取啊,还是被据了;“Reactor”公司HR就记下了你的联系方式,结果出来后会主动打电话通知你:有没有被录取啊,还是被据了;你不用自己打电话去问结果,事实上也不能,你没有HR的留联系方式。
2 Reactor模式的优点Reactor模式是编写高性能网络服务器的必备技术之一,它具有如下的优点:1)响应快,不必为单个同步时间所阻塞,虽然Reactor本身依然是同步的;2)编程相对简单,可以最大程度的避免复杂的多线程及同步问题,并且避免了多线程/进程的切换开销;3)可扩展性,可以方便的通过增加Reactor实例个数来充分利用CPU资源;4)可复用性,reactor框架本身与具体事件处理逻辑无关,具有很高的复用性;3 Reactor模式框架使用Reactor模型,必备的几个组件:事件源、Reactor框架、多路复用机制和事件处理程序,先来看看Reactor模型的整体框架,接下来再对每个组件做逐一说明。
1)事件源Linux上是文件描述符,Windows上就是Socket或者Handle了,这里统一称为“句柄集”;程序在指定的句柄上注册关心的事件,比如I/O事件。
2)event demultiplexer——事件多路分发机制由操作系统提供的I/O多路复用机制,比如select和epoll。
程序首先将其关心的句柄(事件源)及其事件注册到event demultiplexer上;当有事件到达时,event demultiplexer会发出通知“在已经注册的句柄集中,一个或多个句柄的事件已经就绪”;程序收到通知后,就可以在非阻塞的情况下对事件进行处理了。
对应到libevent中,依然是select、poll、epoll等,但是libevent使用结构体eventop进行了封装,以统一的接口来支持这些I/O多路复用机制,达到了对外隐藏底层系统机制的目的。
3)Reactor——反应器Reactor,是事件管理的接口,内部使用event demultiplexer注册、注销事件;并运行事件循环,当有事件进入“就绪”状态时,调用注册事件的回调函数处理事件。
对应到libevent中,就是event_base结构体。
一个典型的Reactor声明方式view plaincopy to clipboardprint?class Reactor{public:int register_handler(Event_Handler *pHandler, int event);int remove_handler(Event_Handler *pHandler, int event);void handle_events(timeval *ptv);// ...};class Reactor{public:int register_handler(Event_Handler *pHandler, int event);int remove_handler(Event_Handler *pHandler, int event);void handle_events(timeval *ptv);// ...};4)Event Handler——事件处理程序事件处理程序提供了一组接口,每个接口对应了一种类型的事件,供Reactor在相应的事件发生时调用,执行相应的事件处理。
通常它会绑定一个有效的句柄。
对应到libevent中,就是event结构体。
下面是两种典型的Event Handler类声明方式,二者互有优缺点。
view plaincopy to clipboardprint?class Event_Handler{public:virtual void handle_read() = 0;virtual void handle_write() = 0;virtual void handle_timeout() = 0;virtual void handle_close() = 0;virtual HANDLE get_handle() = 0;// ...};class Event_Handler{public:// events maybe read/write/timeout/close .etcvirtual void handle_events(int events) = 0;virtual HANDLE get_handle() = 0;// ...};class Event_Handler{public:virtual void handle_read() = 0;virtual void handle_write() = 0;virtual void handle_timeout() = 0;virtual void handle_close() = 0;virtual HANDLE get_handle() = 0;// ...};class Event_Handler{public:// events maybe read/write/timeout/close .etcvirtual void handle_events(int events) = 0;virtual HANDLE get_handle() = 0;// ...};4 Reactor事件处理流程前面说过Reactor将事件流“逆置”了,那么使用Reactor模式后,事件控制流是什么样子呢?可以参见下面的序列图。
5 小结上面讲到了Reactor的基本概念、框架和处理流程,对Reactor有个基本清晰的了解后,再来对比看libevent就会更容易理解了,接下来就正式进入到libevent的代码世界了,加油!参考资料:Pattern-Oriented Software Architecture, Patterns for Concurrent and Networked Objects, Volume 2libevent基本使用场景和事件流程张亮1 前言学习源代码该从哪里入手?我觉得从程序的基本使用场景和代码的整体处理流程入手是个不错的方法,至少从个人的经验上讲,用此方法分析libevent是比较有效的。
2 基本应用场景基本应用场景也是使用libevnet的基本流程,下面来考虑一个最简单的场景,使用livevent 设置定时器,应用程序只需要执行下面几个简单的步骤即可。
1)首先初始化libevent库,并保存返回的指针struct event_base * base = event_init();实际上这一步相当于初始化一个Reactor实例;在初始化libevent后,就可以注册事件了。
2)初始化事件event,设置回调函数和关注的事件evtimer_set(&ev, timer_cb, NULL);事实上这等价于调用event_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发生时,调用该函数执行处理,它有三个参数,调用时由event_base负责传入,按顺序,实际上就是event_set时的fd, event和arg;arg:传递给cb函数指针的参数;由于定时事件不需要fd,并且定时事件是根据添加时(event_add)的超时值设定的,因此这里event也不需要设置。