引言
实现应用的捷径是充分利用开放源代码和开放标准等资源。为了实现视频服务器,研究了开源视频服务器DarwinStreamingServer,Reactor并发编程设计模式,同时还评估了Boost.Asio、ACE、libevent以及 libev等网络编程相关的库。得出的结论是基于DarwinStreamingServer的设计思想,采用Reactor设计模式实现一个更加高效并可扩展的视频服务器,网络库则选择libev。
本文的目的是在理解libev的基础上,学习如何编写基于libev的应用程序。
libev是什么
要了解libev,有必要先了解libevent。早在2000年libevent就发布了第一个版本。通过libevent的编程接口,可以实现在指定的事件发生后调用回调函数,指定的事件是文件描述符上的读写事件等。除了文件描述符的事件,libevent还支持定时器以及信号等。欲详细了解libevent可以参照其站点:http://monkey.org/~provos/libevent/。
以下是libev站点上的自述:
A full-featured and high-performance event loop that is loosely modelled after libevent, but without its limitations and bugs. It is used, among others, in the GNU Virtual Private Ethernet and rxvt-unicode packages, and in the Deliantra MORPG Server and Client.
理解libev
Libev是一个event loop:向libev注册感兴趣的events,比如Socket可读事件,libev会对所注册的事件的源进行管理,并在事件发生时触发相应的程序。
To do this, it must take more or less complete control over your process (or thread) by executing the event loop handler, and will then communicate events via a callback mechanism.
通过event watcher来注册事件,which are relatively small C structures you initialise with the details of the event, and then hand it over to libev by starting the watcher.
先来解释watcher,libev通过分配和注册watcher对不同类型的事件进行监听。不同事件类型的watcher又对应不同的数据类型,watcher的定义模式是
struct
ev_TYPE或者
ev_TYPE,其中TYPE为具体的类型
。当前libev定义了如下类型的watcher:
- ev_io
- ev_timer
- ev_periodic
- ev_signal
- ev_child
- ev_stat
- ev_idle
- ev_prepare and ev_check
- ev_embed
- ev_fork
- ev_cleanup
- ev_async
下面是一个libev使用的例子,通过注册io类型的watcher来监视STDIN可读事件的发生:
接下来看看event loop的概念。示例程序中的ev_run、ev_break以及ev_loop_default都是event loop控制函数。event loop定义为struct ev_loop。有两种类型的event loop,分别是default类型和dynamically created类型,区别是前者支持子进程事件。ev_default_loop和ev_loop_new函数分别用于创建default类型或者dynamically created类型的event loop。
event_run函数告诉系统应用程序开始对事件进行处理,有事件发生时就调用watcher callbacks。除非调用了ev_break或者不再有active的watcher,否则会一直重复这个过程。
libev编程
先来看libev提供的例子。这段代码等待键盘事件的发生,或者超时,两个事件都会触发程序结束。操作系统环境是ubuntu server 10.10,libev是下载的源码,并没有采用ubuntu server自己提供的版本,源码的地址是 http://dist.schmorp.de/libev/libev-4.04.tar.gz。例子代码如下:
// 只需include一个头文件
#include <ev.h>
#include <stdio.h> // for puts
// every watcher type has its own typedef'd struct
// with the name ev_TYPE
ev_io stdin_watcher;
ev_timer timeout_watcher;
// all watcher callbacks have a similar signature
// this callback is called when data is readable on stdin
static void
stdin_cb (EV_P_ ev_io *w, int revents)
{
puts ("stdin ready");
// for one-shot events, one must manually stop the watcher
// with its corresponding stop function.
ev_io_stop (EV_A_ w);
// this causes all nested ev_run's to stop iterating
ev_break (EV_A_ EVBREAK_ALL);
}
// another callback, this time for a time-out
static void
timeout_cb (EV_P_ ev_timer *w, int revents)
{
puts ("timeout");
// this causes the innermost ev_run to stop iterating
ev_break (EV_A_ EVBREAK_ONE);
}
int
main (void)
{
// use the default event loop unless you have special needs
struct ev_loop *loop = EV_DEFAULT;
// initialise an io watcher, then start it
// this one will watch for stdin to become readable
ev_io_init (&stdin_watcher, stdin_cb, 0, EV_READ);
ev_io_start (loop, &stdin_watcher);
// initialise a timer watcher, then start it
// simple non-repeating 5.5 second timeout
ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.);
ev_timer_start (loop, &timeout_watcher);
// now wait for events to arrive
ev_run (loop, 0);
// break was called, so exit
return 0;
}
#include <ev.h>
#include <stdio.h> // for puts
// every watcher type has its own typedef'd struct
// with the name ev_TYPE
ev_io stdin_watcher;
ev_timer timeout_watcher;
// all watcher callbacks have a similar signature
// this callback is called when data is readable on stdin
static void
stdin_cb (EV_P_ ev_io *w, int revents)
{
}
// another callback, this time for a time-out
static void
timeout_cb (EV_P_ ev_timer *w, int revents)
{
}
int
main (void)
{
}
用如下命令编译:
gcc -lev -o sample sample.c
参考资料