Libev学习笔记1

Libev学习笔记1

http://www.cnblogs.com/oyld/p/4368896.html

和Libevent相似,Libev是一个高性事件驱动框架,据说性能比Libevent要高,bug比Libevent要少。Libev只是一个事件驱动框架,不是网络库,因为它的内部并没有任何socket编程代码。支持的事件驱动机制包括:

select
poll
epoll
kqueue
Solaris-specific event port mechanisms

支持的事件类型也很多,下面会全部列出。

官方首页地址:http://software.schmorp.de/pkg/libev.html

安装方法:apt-get install libev-dev

官方文档的第一个例程:

复制代码
// a single header file is required
#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, /*STDIN_FILENO*/ 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;
}
复制代码

这些基于事件的库的使用方法都大同小异:首先建立一个event loop,然后建立需要观察的event,设置好回调函数,将event加入到event loop中,最后run loop。以上程序关注了两类事件:

  1. i/o事件,用ev_io结构体表示。
  2. timer事件,用ev_timer结构体表示。

事件类型的不同,所需的结构体类型也不同,对应关系如下所示:

复制代码
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 TYPES部分给出了详细的说明,需要时自行查阅即可。

首先分析一下struct ev_loop,即event loop,一个线程只能有一个struct ev_loop,它的定义如下所示:

复制代码
#if EV_MULTIPLICITY
  /* 支持多线程时需要定义ev_loop类 */
  struct ev_loop
  {
    ev_tstamp ev_rt_now;
    #define ev_rt_now ((loop)->ev_rt_now)
    #define VAR(name,decl) decl;
      #include "ev_vars.h"
    #undef VAR
  };
  #include "ev_wrap.h"

  static struct ev_loop default_loop_struct;
  EV_API_DECL struct ev_loop *ev_default_loop_ptr = 0; /* needs to be initialised to make it a definition despite extern */

#else
  /* 单线程时所有变量都是全局变量 */
  EV_API_DECL ev_tstamp ev_rt_now = 0; /* needs to be initialised to make it a definition despite extern */
  #define VAR(name,decl) static decl;
    #include "ev_vars.h"
  #undef VAR

  static int ev_default_loop_ptr;

#endif
复制代码

条件编译宏EV_MULTIPLICITY在编译程序时判断程序是否为多线程。当程序为多线程时,也就是上面程序的if分支,由于存在不同的ev_loop,那么就需要struct ev_loop这个结构体进行封装了。当程序为单线程时,也就是上面程序的else分支,整个程序只有唯一的一个ev_loop,它将作为全局变量被所有函数使用。头文件ev_vars.h中定义了各种宏,封装在ev_loop中相当于ev_loop的成员变量,如下所示:

复制代码
struct ev_loop
{
    ev_tstamp ev_rt_now;
    #define ev_rt_now ((loop)->ev_rt_now)
    ev_tstamp now_floor
    ev_tstamp mn_now
    ev_tstamp rtmn_diff
    ...
};
复制代码

而else分支的ev_vars.h没有封装在任何结构体中,因此会声明一大堆全局变量,如下所示:

static ev_tstamp now_floor
static ev_tstamp mn_now
static ev_tstamp rtmn_diff
...

经过深入分析,发现EV_MULTIPLICITY等于1,所以之后的源码分析都是假设EV_MULTIPLICITY=1.

Libev中许多函数定义的形式都是这样的:

static void
timeout_cb (EV_P_ ev_timer *w, int revents)

EV_P_也是为了多线程而存在的,它的定义如下所示:

复制代码
/* support multiple event loops? */
#if EV_MULTIPLICITY
struct ev_loop;
# define EV_P  struct ev_loop *loop               /* a loop as sole parameter in a declaration */
# define EV_P_ EV_P,                              /* a loop as first of multiple parameters */
# define EV_A  loop                               /* a loop as sole argument to a function call */
# define EV_A_ EV_A,                              /* a loop as first of multiple arguments */
# define EV_DEFAULT_UC  ev_default_loop_uc_ ()    /* the default loop, if initialised, as sole arg */
# define EV_DEFAULT_UC_ EV_DEFAULT_UC,            /* the default loop as first of multiple arguments */
# define EV_DEFAULT  ev_default_loop (0)          /* the default loop as sole arg */
# define EV_DEFAULT_ EV_DEFAULT,                  /* the default loop as first of multiple arguments */
#else
# define EV_P void
# define EV_P_
# define EV_A
# define EV_A_
# define EV_DEFAULT
# define EV_DEFAULT_
# define EV_DEFAULT_UC
# define EV_DEFAULT_UC_
# undef EV_EMBED_ENABLE
#endif
复制代码

宏EV_P_最终等于struct ev_loop *loop。当系统支持多线程,每个线程都有一个struct ev_loop时,函数需要区分不同的struct ev_loop,这是就是通过这个宏EV_P_传入某个具体的struct ev_loop。也就是说,当程序为单线程时,函数最终声明为:

static void
timeout_cb (ev_timer *w, int revents)

Libev操纵唯一的一个全局struct ev_loop。当程序支持多线程时,函数最终声明为:

static void
timeout_cb (struct ev_loop *loop, ev_timer *w, int revents)

Libev根据传入的loop操纵对应的struct ev_loop。

 

参考:

http://c4fun.cn/blog/2014/03/06/libev-study/

http://my.oschina.net/u/917596/blog/176658



你可能感兴趣的:(C++)