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

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以及对它的参数进行赋值或者初始化。

你可能感兴趣的:(初始化)