码迷,mamicode.com
首页 > 其他好文 > 详细

libev 默认事件循环初始化的解析

时间:2014-11-02 23:48:18      阅读:462      评论:0      收藏:0      [点我收藏+]

标签:http   io   ar   使用   for   sp   on   art   cti   

libev第一次进入的是默认的事件循环,这里将源码中执行的默认循环流程解析一下,要进入事件循环,如下例子

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;
}

其中ev_loop上一节已经介绍过,这里EV_DEFAULT是一个宏定义,它是一个函数如下

struct ev_loop * ev_default_loop (unsigned int flags) EV_THROW
{
  if (!ev_default_loop_ptr)
    {
#if EV_MULTIPLICITY
      EV_P = ev_default_loop_ptr = &default_loop_struct;
#else
      ev_default_loop_ptr = 1;
#endif

      loop_init (EV_A_ flags);

      if (ev_backend (EV_A))
        {
#if EV_CHILD_ENABLE
          ev_signal_init (&childev, childcb, SIGCHLD);
          ev_set_priority (&childev, EV_MAXPRI);
          ev_signal_start (EV_A_ &childev);
          ev_unref (EV_A); /* child watcher should not keep loop alive */
#endif
        }
      else
        ev_default_loop_ptr = 0;
    }

  return ev_default_loop_ptr;
}

 

首先执行 struct ev_loop *main_loop = ev_default_loop(0); 通过跟进代码可以跟到函数 ev_default_loop 里面去,其主要逻辑,就是全局对象指针ev_default_loop_ptr若为空,也就是不曾使用预制的驱动器时,就让他指向全局对象default_loop_struct,同时在本函数里面统一用名字"loop"来表示该预制驱动器的指针。从而与函数参数为 EV_P 以及 EV_A的写法配合。接着对该指针做 loop_init操作,(这里要注意的是init函数里的一些backend等变量全部是全局变量如#define backend ((loop)->backend)这样定义的)即初始化预制的事件驱动器。这里函数的调用了就是用到了 EV_A_ 这样的写法进行简化。初始化之后如果配置中Libev支持子进程,那么通过信号监控器实现了子进程监控器。这里可以先不用去管他,知道这段代码作用即可。 这里再Libev的函数定义的时候,会看到 “EV_THROW” 这个东西,这里可以不用管它,他是对CPP中"try … throw"的支持,和 EV_CPP(extern "C" {)这样不同寻常的 extern “C” 一样是一种编码技巧。现在我们以分析设计思路为主。在了解了总体后,可以再对其编码技巧进行梳理。否则的话看一份代码会非常吃力,而且速度慢。甚至有的时候这些“hacker”并不一定是有益的。

下面看下驱动器的初始化过程中都做了哪些事情。首先最开始的一段代码判断系统的clock_gettime是否支持CLOCK_REALTIME和CLOCK_MONOTONIC。这两种时间的区别在于后者不会因为系统时间被修改而被修改,详细解释可以参考man page 。接着判断环境变量对驱动器的影响,这个在官方的Manual中有提到,主要就是影响默认支持的IO复用机制。接着是一连串的初始值的赋值,开始不用了解其作用。在后面的分析过程中便可以知道。接着是根据系统支持的IO复用机制,对其进行初始化操作。这里可以去"ev_epoll.c” 和"ev_select.c"中看一下。 最后是判断如果系统需要信号事件,那么通过一个PIPE的IO事件来实现,这里暂且不用管他,在理解了IO事件的实现后,自然就知道这里他做了什么操作。

对于"ev_epoll.c” 和"ev_select.c"中的 xxx_init 其本质是一致的,就像插件一样,遵循一个格式,然后可以灵活的扩展。对于epoll主要就是做了一个 epoll_create*的操作(epoll_create1可以支持EPOLL_CLOEXEC)。

backend_mintime = 1e-3; /* epoll does sometimes return early, this is just to avoid the worst */
backend_modify  = epoll_modify;
backend_poll    = epoll_poll;

这里就可以看成是插件的模板了,在后面会修改的时候调用backend_modify在poll的时候调用backend_poll.从而统一了操作。

epoll_eventmax = 64; /* initial number of events receivable per poll */
epoll_events = (struct epoll_event *)ev_malloc (sizeof (struct epoll_event) * epoll_eventmax)

这个就看做为是每个机制特有的部分。熟悉epoll的话,这个就不用说了。

对于select (Linux平台上的)

backend_mintime = 1e-6;
backend_modify  = select_modify;
backend_poll    = select_poll;

这个和上面一样,是相当于插件接口

vec_ri  = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_ri);
vec_ro  = ev_malloc (sizeof (fd_set));
vec_wi  = ev_malloc (sizeof (fd_set)); FD_ZERO ((fd_set *)vec_wi);
vec_wo  = ev_malloc (sizeof (fd_set));

同样,这个是select特有的,表示读和写的fd_set的vector,ri用来装select返回后符合条件的部分。其他的如poll、kqueue、Solaris port都是类似的,可以自行阅读。

其中的EV_A_和EV_P定义如下:

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 */

 

这样EV_P就可以作为函数的参数传入,如unsigned int ev_backend (EV_P) EV_THROW,而EV_A可以作为函数调用时传入,ev_backend (EV_A)。这里应该也是作者的技巧。总而言之就是操作的一个全局变量struct ev_loop * loop以及对它的参数进行赋值或者初始化。

libev 默认事件循环初始化的解析

标签:http   io   ar   使用   for   sp   on   art   cti   

原文地址:http://www.cnblogs.com/pang1567/p/4070073.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!