libev源码解读

版权声明:本文为博主原创文章,未经博主允许不得转载,否则博主会很生气,后果会很严重。 https://blog.csdn.net/drdairen/article/details/53785447

1、源码:
源码参见官网或者我的github
2、安装使用:
安装与基本使用参见我的另一篇博客:传送门
3、架构
Libev通过一个 struct ev_loop结结构表示一个事件驱动的框架。在这个框架里面通过ev_xxx结构,ev_init、ev_xxx_set、ev_xxx_start接口向这个事件驱动的框架里面注册事件监控器,当相应的事件监控器的事件出现时,便会触发该事件监控器的处理逻辑,去处理该事件。处理完之后,这些监控器进入到下一轮的监控中。符合一个标准的事件驱动状态的模型。
4、源码
对于libev这样组件类的网络库阅读源码,我是按照以下的顺序:
1.官方文档2.看api写几个示例程序3.浏览代码,搞清楚结构组织4.根据API中的接口去源码中看看是怎么实现的,主干是什么5.把源码中demo没有的功能自己测试使用下

libev库是用c代码编写的,共有八千行左右,下面分析每个文件
ev.c:是libev的主要逻辑,大概有五千行,
ev_select.c:包括ev_select.c在内的这几个都是对系统提供的IO复用机制的支持这里写图片描述
ev.h:是对一些接口api和变量、常量的定义
ev_wrap.h:各种宏定义,对各种变量的引用的掩盖、封装
ev.vars.h:结构的成员变量定义所有的这些代码大致都是分为3个部分,1、一连串的宏定义,根据环境定义哪些状态变量2、针对不同的硬件平台及编译器的跨平台实现3、程序的主要逻辑
如图这里写图片描述
所以我们可以直接看代码结构部分即可,具体分析如下:

首先我们来看ev.h,其中的对于每一项事件都有一个结构体/类的定义,先看一下基类watcher是怎么定义的(都在ev.h中):

/* base class, nothing to see here unless you subclass */
typedef struct ev_watcher
{
  EV_WATCHER (ev_watcher)
} ev_watcher;

其中宏定义EV_WATCHER (ev_watcher)定义如下:

/* **shared by all watchers** */
#define EV_WATCHER(type)            \
  int active; /* private */         \
  int pending; /* private */            \
  EV_DECL_PRIORITY /* private */        \
  EV_COMMON /* rw */                \
  EV_CB_DECLARE (type) /* private */

实际上可以看做如下:

typedef struct ev_watcher
{
    int active; 
    int pending;
    int priority;
    void *data;    
    void (*cb)(struct ev_loop *loop, struct ev_watcher *w, int revents);
} ev_watcher;

宏定义太多了反而不利于我们阅读,借助Windows下的IDE或者Linux下vim emacs或者vim+cscope或者vim+ctags来阅读下载的源码,可以很方便的找到变量的定义位置。
除了watcher的基类,还有一个监控器list类:

typedef struct ev_watcher_list
{
  EV_WATCHER_LIST (ev_watcher_list)
} ev_watcher_list;

其也是基于上面EV_WATCHER(type),展开也就是如下:

typedef struct ev_watcher_list
{
    int active; 
    int pending;
    int priority;
    void *data;    
    void (*cb)(struct ev_loop *loop, struct ev_watcher_list *w, int revents);
    struct ev_watcher_list *next;
} ev_watcher_list;

同理,再看一个io watcher:

typedef struct ev_io
{
  EV_WATCHER_LIST (ev_io)

  int fd;     /* ro */
  int events; /* ro */
} ev_io;

展开就是:

typedef struct ev_io
{
    int active; 
    int pending;
    int priority;
    void *data;    
    void (*cb)(struct ev_loop *loop, struct ev_io *w, int revents);
    struct ev_watcher_list *next;

    int fd;
    int events; 
} ev_io;

这里的fd,events就是派生类的私有成员,分别表示监听的文件描述符fd和触发的事件,可以看到,所有派生类的私有变量放在公共变量的后面,这样,在使用c语言的指针强制转换的时候,若把一个派生类ev_io类型对象的指针赋值给一个基类ev_watcher类型的指针p后,就可以通过p->active,p->pending,p->priority等来访问派生类中的active成员了。(不是多态,我想多了)
同样,定时器的watcher直接写出如下:

typedef struct ev_watcher_time
{
    int active; 
    int pending;
    int priority;
    void *data;    
    void (*cb)(struct ev_loop *loop, struct ev_watcher_time *w, int revents);

    ev_tstamp at;
} ev_watcher_time;

这个at就是派生类中新的自有成员 ,表示的是at时间后,触发这个定时器,at类型为double
信号signal的watcher如下:

typedef struct ev_signal
{
    int active; 
    int pending;
    int priority;
    void *data;    
    void (*cb)(struct ev_loop *loop, struct ev_signal *w, int revents);
    struct ev_watcher_list *next;

    int signum; 
} ev_signal;

这个signum就是派生类中新的自有成员 ,当给定的signum信号接收到时,触发调用
其他的事件watcher跟这三大事件类似,也就是:

**ev_periodic:**invoked at some specific time, possibly repeating at regular intervals (based on UTC)
**ev_child:**invoked when sigchld is received and waitpid indicates the given pid
**ev_stat:**invoked each time the stat data changes for a given path
**ev_fork:**the callback gets invoked before check in the child process when a fork was detected

**ev_idle:**invoked when the nothing else needs to be done, keeps the process from blocking
ev_prepare:
invoked for each run of the mainloop, just before the blocking call ,you can still change events in any way you like
**ev_check:**invoked for each run of the mainloop, just after the blocking call
**ev_cleanup:**is invoked just before the loop gets destroyed
ev_embed:
used to embed an event loop inside another ,the callback gets invoked when the event loop has handled events, and can be 0
**ev_async:**invoked when somebody calls ev_async_send on the watcher
我数了一下,包括3个常用类型,一共是13个事件类型,代码中还定义了一个可以容纳所有watcher对象的类型ev_any_watcher,定义如下:

union ev_any_watcher
{
  struct ev_watcher w;
  struct ev_watcher_list wl;

  struct ev_io io;
  struct ev_timer timer;
  struct ev_periodic periodic;
  struct ev_signal signal;
  struct ev_child child;
#if EV_STAT_ENABLE
  struct ev_stat stat;
#endif
#if EV_IDLE_ENABLE
  struct ev_idle idle;
#endif
  struct ev_prepare prepare;
  struct ev_check check;
#if EV_FORK_ENABLE
  struct ev_fork fork;
#endif
#if EV_CLEANUP_ENABLE
  struct ev_cleanup cleanup;
#endif
#if EV_EMBED_ENABLE
  struct ev_embed embed;
#endif
#if EV_ASYNC_ENABLE
  struct ev_async async;
#endif
};

/////////////////////////////割割割割割割割割////////////////////////////
下面再看ev.c中主循环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
  };

其中ev_tstamp 就是时间单位,实际上就是double类型

把宏定义替换后,看到完整的ev_loop定义如下:

struct ev_loop
{
    ev_tstamp ev_rt_now;
    ev_tstamp now_floor;
    int rfeedmax;
    ... .........;
}

类似这种加很多宏定义的
ev.h中还有如下的方便编程的define,可以使代码简洁不少,但同时也增加了阅读的难度。

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

在struct ev_loop的定义下面可以看到这样一句:

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

该操作就是创建了一个ev_loop类型的实例对象以及ev_loop类型的空指针,default_loop_struct表示的是预制事件驱动器(就是loop)。如果在代码中使用的是预制事件驱动器,那么后续的操作就都围绕着这个数据结构展开了。
注意ev_default_loop_ptr是一个全局变量
//////////////////////////////////////////////////////////
下面我们用一个只有io事件的例子,通过gdb设置断点单步调试,来观察代码的过程,测试demo如下:

#include<ev.h>
#include <stdio.h>
#include <signal.h>
#include <sys/unistd.h>

ev_io io_w;
void io_action(struct ev_loop *main_loop,ev_io *io_w,int e)
{
        int rst;
        char buf[1024] = {'\0'};
        puts("in io cb\n");
        read(STDIN_FILENO,buf,sizeof(buf));
        buf[1023] = '\0';
        printf("Read in a string %s \n",buf);
        ev_io_stop(main_loop,io_w);

}
int main(int argc ,char *argv[])
{
    struct ev_loop *main_loop = ev_default_loop(0);

    ev_init(&io_w,io_action);
    ev_io_set(&io_w,STDIN_FILENO,EV_READ);  
    ev_io_start(main_loop,&io_w);

    ev_run(main_loop,0);

    return 0;
}

编译该程序,注意带上-g参数,这样就是把调试信息加到可执行文件中,如果没有-g,你将看不见程序的函数名、变量名,所代替的全是运行时的内存地址。编译命令如下:

gcc -g libev_io.c -l ev -o libio

启动gdb调试:
启动gdb

我们从main开始一步步走。首先执行 struct ev_loop *main_loop = ev_default_loop(0)这一句,通过跟进代码可以跟到函数 ev_default_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;
}

首先判断上面说的在ev_loop中定义的全局对象指针ev_default_loop_ptr是否为空,也就是不曾使用预制的驱动器时,就让他指向静态变量default_loop_struct。
EV_P = ev_default_loop_ptr = &default_loop_struct;这一句表明同时在本函数里面统一用名字”loop”来表示该预制驱动器的指针。从而与函数参数为 EV_P 以及 EV_A的写法配合。也即是:

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

接着对该指针做 loop_init操作,即初始化预制的事件驱动器。这里函数的调用了也是用到了 EV_A这样的写法进行简化。
初始化之后如果配置中Libev支持子进程,那么通过信号监控器实现了子进程监控器。
注意,在Libev的函数定义的时候,会看到 “EV_THROW” 这个东西,这里可以不用管它,他是对CPP中”try … throw”的支持,和EV_CPP中的extern “C”一样是一种编码技巧。

下面看看驱动器loop的初始化过程,也就是这一句:loop_init (EV_A_ flags);
比较长,很多都是根据环境判断宏定义,来确定是否执行,代码如下:

/* initialise a loop structure, must be zero-initialised */
static void noinline ecb_cold
loop_init (EV_P_ unsigned int flags) EV_THROW
{
  if (!backend)
    {
      origflags = flags;

#if EV_USE_REALTIME
      if (!have_realtime)
        {
          struct timespec ts;

          if (!clock_gettime (CLOCK_REALTIME, &ts))
            have_realtime = 1;
        }
#endif

#if EV_USE_MONOTONIC
      if (!have_monotonic)
        {
          struct timespec ts;

          if (!clock_gettime (CLOCK_MONOTONIC, &ts))
            have_monotonic = 1;
        }
#endif

      /* pid check not overridable via env */
#ifndef _WIN32
      if (flags & EVFLAG_FORKCHECK)
        curpid = getpid ();
#endif

      if (!(flags & EVFLAG_NOENV)
          && !enable_secure ()
          && getenv ("LIBEV_FLAGS"))
        flags = atoi (getenv ("LIBEV_FLAGS"));

      ev_rt_now          = ev_time ();
      mn_now             = get_clock ();
      now_floor          = mn_now;
      rtmn_diff          = ev_rt_now - mn_now;
#if EV_FEATURE_API
      invoke_cb          = ev_invoke_pending;
#endif

      io_blocktime       = 0.;
      timeout_blocktime  = 0.;
      backend            = 0;
      backend_fd         = -1;
      sig_pending        = 0;
#if EV_ASYNC_ENABLE
      async_pending      = 0;
#endif
      pipe_write_skipped = 0;
      pipe_write_wanted  = 0;
      evpipe [0]         = -1;
      evpipe [1]         = -1;
#if EV_USE_INOTIFY
      fs_fd              = flags & EVFLAG_NOINOTIFY ? -1 : -2;
#endif
#if EV_USE_SIGNALFD
      sigfd              = flags & EVFLAG_SIGNALFD  ? -2 : -1;
#endif

      if (!(flags & EVBACKEND_MASK))
        flags |= ev_recommended_backends ();

#if EV_USE_IOCP
      if (!backend && (flags & EVBACKEND_IOCP  )) backend = iocp_init   (EV_A_ flags);
#endif
#if EV_USE_PORT
      if (!backend && (flags & EVBACKEND_PORT  )) backend = port_init   (EV_A_ flags);
#endif
#if EV_USE_KQUEUE
      if (!backend && (flags & EVBACKEND_KQUEUE)) backend = kqueue_init (EV_A_ flags);
#endif
#if EV_USE_EPOLL
      if (!backend && (flags & EVBACKEND_EPOLL )) backend = epoll_init  (EV_A_ flags);
#endif
#if EV_USE_POLL
      if (!backend && (flags & EVBACKEND_POLL  )) backend = poll_init   (EV_A_ flags);
#endif
#if EV_USE_SELECT
      if (!backend && (flags & EVBACKEND_SELECT)) backend = select_init (EV_A_ flags);
#endif

      ev_prepare_init (&pending_w, pendingcb);

#if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE
      ev_init (&pipe_w, pipecb);
      ev_set_priority (&pipe_w, EV_MAXPRI);
#endif
    }
}

开始是根据系统变量或者环境来选择性的执行一些步骤,这些在官方的Manual中有提到,主要就是影响默认支持的IO复用机制。接着是一连串的初始值的赋值,接着是根据系统支持的IO复用机制,对其进行初始化操作。
譬如如果选择select进行io复用,则会执行如下的初始化过程:

if (!backend && (flags & EVBACKEND_SELECT)) backend = select_init (EV_A_ flags);

select_init的过程具体在ev_select.c中,如下:

int inline_size
select_init (EV_P_ int flags)
{
//确定了要用的io复用方式,首先把名字统一了
  backend_mintime = 1e-6;
  backend_modify  = select_modify;
  backend_poll    = select_poll;
//读和写的fd_set的vector  ri用来装select函数返回后符合条件的fd
#if EV_SELECT_USE_FD_SET
  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));
  #ifdef _WIN32
  vec_eo  = ev_malloc (sizeof (fd_set));
  #endif
#else
  vec_max = 0;
  vec_ri  = 0;
  vec_ro  = 0;
  vec_wi  = 0;
  vec_wo  = 0;
  #ifdef _WIN32
  vec_eo  = 0;
  #endif
#endif

  return EVBACKEND_SELECT;
}

最后是判断如果系统需要信号事件,那么进行一系列的操作。

下面看我们的demo下一句监控器初始化:
ev_init(&io_w,io_action);
经过跟踪查看,发现这不是一个函数,仅仅是一个宏定义:

/* these may evaluate ev multiple times, and the other arguments at most once */
/* **either use ev_init + ev_TYPE_set, or the ev_TYPE_init macro, below, to first initialise a watcher** */
#define ev_init(ev,cb_) do {            \
  ((ev_watcher *)(void *)(ev))->active  =   \
  ((ev_watcher *)(void *)(ev))->pending = 0;    \
  ev_set_priority ((ev), 0);            \
  ev_set_cb ((ev), cb_);            \
} while (0)

再看demo下一句,设置io watcher的触发条件:
ev_io_set(&io_w,STDIN_FILENO,EV_READ);
跟踪发现该句也是一个宏定义:

#define ev_io_set(ev,fd_,events_)            do { (ev)->fd = (fd_); (ev)->events = (events_) | EV__IOFDSET; } while (0)

即把检测的文件描述符跟事件类型赋值
下面看demo中io观察器配置的最后一步:
ev_io_start(main_loop,&io_w);
在ev.c文件中可以看到,该函数定义如下:
ev_io_start:

void noinline
ev_io_start (EV_P_ ev_io *w) EV_THROW
{
  int fd = w->fd;

  if (expect_false (ev_is_active (w)))
    return;

  assert (("libev: ev_io_start called with negative fd", fd >= 0));
  assert (("libev: ev_io_start called with illegal event mask", !(w->events & ~(EV__IOFDSET | EV_READ | EV_WRITE))));

  EV_FREQUENT_CHECK;

  **ev_start (EV_A_ (W)w, 1);**
  array_needsize (ANFD, anfds, anfdmax, fd + 1, array_init_zero);
  wlist_add (&anfds[fd].head, (WL)w);

  /* common bug, apparently */
  assert (("libev: ev_io_start called with corrupted watcher", ((WL)w)->next != (WL)w));

  fd_change (EV_A_ fd, w->events & EV__IOFDSET | EV_ANFD_REIFY);
  w->events &= ~EV__IOFDSET;

  EV_FREQUENT_CHECK;
}

首先是两个assert,就是用来检测错误的,可能是检查内部数据结构啊,边界值是否合理等等。
再下面就是ev_start,其代码如下:

inline_speed void
ev_start (EV_P_ W w, int active)
{
  pri_adjust (EV_A_ w);
  w->active = active;
  ev_ref (EV_A);
}

在这里面把active置1,并且++了activecnt,activecnt也就是指事件驱动器loop上面存在的监控器数量,因为现在新建了一个io watcher,所以数量要加1,而active就是指watcher是否正在监控。
下面就是:

array_needsize (ANFD, anfds, anfdmax, fd + 1, array_init_zero);
  wlist_add (&anfds[fd].head, (WL)w);

这就是libev里面对动态数组的实现,判断anfd数组的空间是否足够fd+1,不够的话就调整数组内存的大小。其中ADFD结构的定义如下:

/* file descriptor info structure */
typedef struct
{
  WL head;
  unsigned char events; /* the events watched for */
  unsigned char reify;  /* flag set when this ANFD needs reification (EV_ANFD_REIFY, EV__IOFDSET) */
  unsigned char emask;  /* the epoll backend stores the actual kernel mask in here */
  unsigned char unused;
#if EV_USE_EPOLL
  unsigned int egen;    /* generation counter to counter epoll bugs */
#endif
#if EV_SELECT_IS_WINSOCKET || EV_USE_IOCP
  SOCKET handle;
#endif
#if EV_USE_IOCP
  OVERLAPPED or, ow;
#endif
} ANFD;

这是一个文件描述符信息的数据结构,包括WL类型也就是watcher的基类链表:ev_watcher_list *类型的变量head,一个ANFD就是一个文件描述符所关联的信息,前面anfds就是ANFD类型的数组,数组的下标是通过fd进行索引的,这样由于fd不会太大,所以分配的空间合理,索引速度也是o(1)级别的。
再看下面:

  fd_change (EV_A_ fd, w->events & EV__IOFDSET | EV_ANFD_REIFY);
  w->events &= ~EV__IOFDSET;

对于fd_change:

/* something about the given fd changed */
inline_size void
fd_change (EV_P_ int fd, int flags)
{
  unsigned char reify = anfds [fd].reify;
  anfds [fd].reify |= flags;

  if (expect_true (!reify))
    {
      ++fdchangecnt;
      array_needsize (int, fdchanges, fdchangemax, fdchangecnt, EMPTY2);
      fdchanges [fdchangecnt - 1] = fd;
    }
}

**fd_change:如果对于给定的fd,在其上的event发生了,那么就把fd添加到一个fdchanges的数组里。
fd_reify:依次遍历fdchanges数组中的fd,并且比较fd上的handle与对应anfd上的handle是否相同,从而判断监控条件是否发生改变,如果改变了则调用backend_modify也就是 select_modify或者epoll_ctl等调整系统对该fd的监控条件,代码如下:**

SOCKET handle = EV_FD_TO_WIN32_HANDLE (fd);

if (handle != anfd->handle)
 {
   unsigned long arg;
    assert (("libev: only socket fds supported in this configuration", ioctlsocket (handle, FIONREAD, &arg) == 0));

   /* handle changed, but fd didn't - we need to do it in two steps */
   backend_modify (EV_A_ fd, anfd->events, 0);
   anfd->events = 0;
   anfd->handle = handle;
 }

fdchanges数组的作用就在于此,该数组记录了anfds数组中fd对应的watcher监控条件可能被修改的文件描述符,并且在适当的时候低啊用系统的io复用机制修改系统的监控条件。正如官方代码注释中说的:
make sure the external fd watch events are in-sync with the kernel/libev internal state
具体的anfd和fdchanges结构如下:
结构图
至此,把io事件的注册过程(ev_io_start)走了一遍,大致就是从之前设置了监控条件的io watcher获取文件描述符fd,找到fd在anfds中对应的ANFD结构,然后把watcher加入到该结构的ev_watcher_list *类型的head链上去。因为对应该fd的监控条件肯定已经改变了(新加入的啊),所以在fdchanges数组中加入该fd,然后后续会通过io复用机制来修改对该fd的监控条件。

/////////////////////////////////////////////////////////////////////////////////
下面就是启动我们的时间驱动器,也就是定义的loop/mainloop:
ev_run(main_loop,0);
这个函数直接从ev.c中就可以看到,比较长就不全部贴上来了,大致的逻辑就是

do{

    //fork prepare...
    XXXXXX;

    /* update fd-related kernel structures */
    fd_reify (EV_A);

     /* calculate blocking time */
     XXXXXxxxX;
     //进入poll之前,先sleep io_blocktime
    backend_poll();//也就是select_poll或者poll_poll或者epoll_poll

}while(condition_is_ok)

先计算出poll之前要阻塞的时间,进入poll之前,先sleep io_blocktime,然后就进入backend_poll()函数,也就是select_poll或者poll_poll或者epoll_poll,我们以epoll举例,epoll共有3个系统调用:
1)调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)
2)调用epoll_ctl向epoll对象中添加这100万个连接的套接字
3)调用epoll_wait收集发生的事件的连接
具体select poll epoll的使用与分析见我的另一篇博客:
传送门

我们进入backend_poll也就是epoll_poll,首先进入epoll_wait()去收集发生的事件连接,返回发生的事件数目eventcnt,然后依次遍历所有发生的事件,分别做处理。
从epoll返回后,将监控中的文件描述符fd以及其pending(满足监控)的条件通过fd_event()做判断监控条件是否改变,然后到fd_event_nocheck()里面对anfds[fd]数组中的fd上的挂的监控器依次做检测,如果pending条件符合,便通过ev_feed_event()将该监控器加入到pendings数组中pendings[pri]上的pendings[pri][old_lenght+1]的位置。ev_feed_event()代码如下:

void noinline
ev_feed_event (EV_P_ void *w, int revents) EV_THROW
{
  W w_ = (W)w;
  int pri = ABSPRI (w_);

  if (expect_false (w_->pending))
    pendings [pri][w_->pending - 1].events |= revents;
  else
    {
      w_->pending = ++pendingcnt [pri];
      array_needsize (ANPENDING, pendings [pri], pendingmax [pri], w_->pending, EMPTY2);
      pendings [pri][w_->pending - 1].w      = w_;
      pendings [pri][w_->pending - 1].events = revents;
    },
  pendingpri = NUMPRI - 1;
}

这里要介绍一个新的数据结构,他表示pending中的监控条件满足了,但是还没有触发动作的状态:

/* stores the pending event set for a given watcher */
typedef struct
{
  W w;
  int events; /* the pending event set for the given watcher */
} ANPENDING;

W就是 ev_watcher * 类型,上面用到的pendings[]就是ANPENDING类型的一个二维数组,其一级下标pri就是指watcher的优先级,该优先级上pending的监控器数目为二级下标,对应监控器中实际的pending值就是二维下标+1。
首先对于二维数组pendings[],其定义为ANPENDING *pendings [NUMPRI],其中以优先级为下标的每个元素都是ANPENDING类型的,这样就可以把pending的event类型及watcher指针加入到二维数组pendings[]里去,这是通过xxx_reify函数实现这个过程的。
下图就是anfds数组跟pendings数组的对应结构与关系:
pendings
再后面就是执行:
EV_INVOKE_PENDING;
其实就是invoke_cb (EV_A),也就是调用loop->invoke_cb,也就是ev_invoke_pending()函数,其代码如下:

void noinline
ev_invoke_pending (EV_P)
{
  pendingpri = NUMPRI;

  while (pendingpri) /* pendingpri possibly gets modified in the inner loop */
    {
      --pendingpri;

      while (pendingcnt [pendingpri])
        {
          ANPENDING *p = pendings [pendingpri] + --pendingcnt [pendingpri];

          p->w->pending = 0;
          EV_CB_INVOKE (p->w, p->events);
          EV_FREQUENT_CHECK;
        }
    }
}

该函数会遍历pendings这个二维数组,按优先级执行在pending的每一个watcher上触发事件对应的的回调函数。遍历结束后判断是否结束整个loop,不的话就再次从io复用那儿等待来一遍,结束的话就清理、退出。

//////////////////////////////////////////////////////////////////
至此,我们就把一个简单的io watcher的例子看完了,其他的事件观察器也基本类似,整个逻辑都是围绕watcher来做的,libev内部维护一个基类ev_watcher和一些特定监控器的派生类ev_xxx,如ev_io,当我们要使用一个监控器的时候,首先生成一个具体的watcher实例,并且通过派生类的私有成员设置触发条件,监控fd。
然后就用anfds或者最小堆管理这些watchers,然后通过backend_poll代表的系统io复用机制如epoll,以及时间堆管理运算出pending的watcher,再调用reify函数把触发事件的watcher按优先级加入到pendings[]二维数组中去。
最后就是依次按优先级调用pendings里面watcher上触发事件的回调函数,这样就实现了一个按优先级的事件模型。
整个过程如下图所示:
libev

////////////////////////////////////////////////////////////////////////////

猜你喜欢

转载自blog.csdn.net/drdairen/article/details/53785447