使用Libev
Libev的作者写了一份很好的官方Manual,比较的齐全,即介绍了Libev的设计思想,也介绍了基本使用还包括内部各类事件详细介绍。这里略微赘述一下。Libev通过一个 ·struct ev_loop· 结结构表示一个事件驱动的框架。在这个框架里面通过ev_xxx
结构,ev_init
、ev_xxx_set
、ev_xxx_start
接口箱这个事件驱动的框架里面注册事件监控器,当相应的事件监控器的事件出现时,便会触发该事件监控器的处理逻辑,去处理该事件。处理完之后,这些监控器进入到下一轮的监控中。符合一个标准的事件驱动状态的模型。
Libev 除了提供了基本的三大类事件(IO事件、定时器事件、信号事件)外还提供了周期事件、子进程事件、文件状态改变事件等多个事件,这里我们用三大基本事件写一个例子,和Manual上的类似,但是没有做收尾工作,为的是将事件的框架清晰的呈现出来。
04 |
#include <sys/unistd.h> |
10 |
void io_action(struct ev_loop *main_loop,ev_io *io_w, int e) |
13 |
char buf[ 1024 ] = { '\0' }; |
15 |
read(STDIN_FILENO,buf,sizeof(buf)); |
17 |
printf( "Read in a string %s \n" ,buf); |
18 |
ev_io_stop(main_loop,io_w); |
22 |
void timer_action(struct ev_loop *main_loop,ev_timer *timer_w, int e) |
24 |
puts( "in tiemr cb \n" ); |
25 |
ev_timer_stop(main_loop,timer_w); |
29 |
void signal_action(struct ev_loop *main_loop,ev_signal signal_w, int e) |
31 |
puts( "in signal cb \n" ); |
32 |
ev_signal_stop(main_loop,signal_w); |
33 |
ev_break(main_loop,EVBREAK_ALL); |
36 |
int main( int argc , char *argv[]) |
38 |
struct ev_loop *main_loop = ev_default_loop( 0 ); |
39 |
ev_init(&io_w,io_action); |
40 |
ev_io_set(&io_w,STDIN_FILENO,EV_READ); |
41 |
ev_init(&timer_w,timer_action); |
42 |
ev_timer_set(&timer_w, 2 , 0 ); |
43 |
ev_init(&signal_w,signal_action); |
44 |
ev_signal_set(&signal_w,SIGINT); |
46 |
ev_io_start(main_loop,&io_w); |
47 |
ev_timer_start(main_loop,&timer_w); |
48 |
ev_signal_start(main_loop,&signal_w); |
下面对使用到的这些API进行说明。
这里使用了3种事件监控器,分别监控IO事件、定时器事件以及信号事件。因此定义了3个监控器(watcher),以及触发监控器时要执行动作的回调函数。Libev定义了多种监控器,命名方式为ev_xxx
这里xxx代表监控器类型,其实现是一个结构体,
通过宏定义可以简写为 ev_xxx
。回调函数的类型为 void cb_name(struct ev_loop *main_loop,ev_xxx *io_w,int event)
。
在main中,首先定义了一个事件驱动器的结构 struct ev_loop *main_loop
这里调用 ev_default_loop(0)
生成一个预制的全局驱动器。这里可以参考Manual中的选择。然后依次初始化各个监控器以及设置监控器的触发条件。
初始化监控器的过程是将相应的回调函数即触发时的动作注册到监控器上。
设置触发条件则是该条件产生时才去执行注册到监控器上的动作。对于IO事件,一般是设置特定fd上的的可读或可写事件,定时器则是多久后触发。这里定时器的触发条件中还有第三参数,表示第一次触发后,是否循环,若为0则吧循环,否则按该值循环。信号触发器则是设置触发的信号。
在初始化并设置好触发条件后,先调用ev_xxx_start
将监控器注册到事件驱动器上。接着调用 ev_run
开始事件驱动器。
在事件的触发动作里面。我加入了一个 ev_xxx_stop
函数,与上面对应,也就是讲改监控器从事件驱动器里面注销掉。使其不再起作用。而在信号触发的动作中还加入了一个ev_break
该函数可以使进程跳出 main_loop
事件驱动器循环,也就是关闭事件驱动器。结束这一逻辑。
libev最简单的示例就是这样的一个结构。定义一个监控器、书写触发动作逻辑、初始化监控器、设置监控器触发条件、将监控器加入大事件驱动器的循环中即可。一个比较清晰的事件驱动框架。
libev的事件驱动过程可以想象成如下的伪代码:
4 |
t = caculate_loop_time() |
6 |
deal_with_pending_event() |
首先做一些初始化操作,然后进入到循环中,该循环通过一个状态位来控制是否执行。在循环中,计算出下一次轮询的时间,这里轮询的实现就采用了系统提供的epoll、kqueue等机制。再轮询结束后检查有哪些监控器的被触发了,依次执行触发动作。这里不要纠结信号事件、定时器时间咋都经过了deal_loop
libev是如何实现的这里暂且不讨论,这个伪代码只是大致表示下libev的整体框架。