linux环境下libev网络库的安装和使用

首先介绍linux环境下,如何编译安装libev,并编译成功官方示例程序,这里使用的是ubunu系统。

1.下载libev开发包,libev的官方网站是  http://dist.schmorp.de/libev/(需要墙出去),可以去我的微云https://share.weiyun.com/5tTAxyN-g,下载对应版本。

2、这里选择的版本为libev-4.24.tar.gz,解压开发包:

1)tar zxvf libev-4.24.tar.gz

进入解压后的文件夹

2) cd libev-4.24/

3) ./configure #生成makefile,可以通过./configure prefix=/XXX 来设定静态库存放的文件夹,默认情况下为/usr/local/lib

4)make #编译

5)make install #安装

查看/usr/local/lib和和/usr/local/include文件夹可以看到包含了libev编程必须的库文件以及头文件,默认是可见的,不要修改环境变量。

3、编译运行第一个官方示例:

// libev需要的头文件
#include <ev.h>
#include <stdio.h>
 
// 建立我们刚刚说的需要监听的事件,这些事件类型是libev提供的
// with the name ev_TYPE
//ev_io和ev_timer最为常用,ev_io为监听控制台输入,ev_timer为时间事件
ev_io stdin_watcher;
ev_timer timeout_watcher;
 
// 以下为自定义的回调函数,当触发监听事件时,调用执行对应的函数
 
// ev_io事件的回调函数,当有输入流stdin时,调用函数
static void stdin_cb (EV_P_ ev_io *w, int revents)
{
puts ("stdin ready");
//对ev_io事件的监控不会自动停止,需要手动在需要的时候停止
ev_io_stop (EV_A_ w);
 
//整体的loop事件在所有监控停止时停止,也可以手动关闭全部的ev_run
ev_break (EV_A_ EVBREAK_ALL);
}
 
// 时间事件的自定义回调函数,可定时触发
static void timeout_cb (EV_P_ ev_timer *w, int revents)
{
puts ("timeout");
//关闭最早的一个还在运行的ev_run
ev_break (EV_A_ EVBREAK_ONE);
}
 
int main (void)
{
//定义默认的 event loop,它就像一个大容器,可以装载着很多事件不停运行
struct ev_loop *loop = EV_DEFAULT;
 
// 初始化ev_io事件监控,设置它的回调函数,和stdin
ev_io_init (&stdin_watcher, stdin_cb,/*STDIN_FILENO*/ 0, EV_READ);
//将ev_io事件放到event loop里面运行
ev_io_start (loop, &stdin_watcher);
 
// 初始化ev_timer事件监控,设置它的回调函数,间隔时间,是否重复
ev_timer_init (&timeout_watcher, timeout_cb, 5.5,0.);
//将ev_timer事件放到event loop里面运行
ev_timer_start (loop, &timeout_watcher);
 
// 将我们的大容器event loop整体运行起来
ev_run (loop, 0);
 
// ev_run运行结束之后,才会运行到这里
return 0;
}

文件名为:libevtest.c,使用gcc编译:

gcc libevtest.c -o libevtest -lev 完成编译。

对于出现error while loading shared libraries: libev.so.4: cannot open shared object file: No such file or directory的情况,主要是因为系统不知道.so文件放在哪里,这时候就要在/etc/ld.so.conf中加入xxx.so所在的目录。一般而言,有很多的so会存放在/usr/local/lib这个目录底下,去这个目录底下找,果然发现自己所需要的.so文件。在/etc/ld.so.conf中加入/usr/local/lib这一行,保存之后,再运行:/sbin/ldconfig -v更新一下配置即可。
 

4.下面对示例代码进行讲解,libev的核心是event loop,event loop可以认为是一个大的while循环,它可以监听各种事件,并在事件发生时调用绑定的对应注册函数,用结构体struct ev_loop表示,Libev 支持两种 loop,一种是 default loop:

struct ev_loop *ev_default_loop (unsigned int flags)

它支持 child process event,此函数不是线程安全的。另一个是动态创建的 event loops :

struct ev_loop *ev_loop_new (unsigned int flags);

此函数线程安全,通常每个线程使用一个loop,此loop不支持child process event,示例中创建的是默认event loop。

libev支持多种功能,比如文件状态监控、定时器等。这些功能都有其相对应的一个“监视器”(watcher),比如文件监视器、相对时间定时器监视器等。虽然这些监视器很多,但是它们都共有一些属性, 在ev.h文件中可以看到:

#ifndef EV_COMMON
# define EV_COMMON void *data;
#endif

#ifndef EV_CB_DECLARE
# define EV_CB_DECLARE(type) void (*cb)(EV_P_ struct type *w, int revents);
#endif
#ifndef EV_CB_INVOKE
# define EV_CB_INVOKE(watcher,revents) (watcher)->cb (EV_A_ (watcher), (revents))
#endif

/* not official, do not use */
#define EV_CB(type,name) void name (EV_P_ struct ev_ ## type *w, int revents)

/*
 * struct member types:
 * private: you may look at them, but not change them,
 *          and they might not mean anything to you.
 * ro: can be read anytime, but only changed when the watcher isn't active.
 * rw: can be read and modified anytime, even when the watcher is active.
 *
 * some internal details that might be helpful for debugging:
 *
 * active is either 0, which means the watcher is not active,
 *           or the array index of the watcher (periodics, timers)
 *           or the array index + 1 (most other watchers)
 *           or simply 1 for watchers that aren't in some array.
 * pending is either 0, in which case the watcher isn't,
 *           or the array index + 1 in the pendings array.
 */

#if EV_MINPRI == EV_MAXPRI
# define EV_DECL_PRIORITY
#elif !defined (EV_DECL_PRIORITY)
# define EV_DECL_PRIORITY int priority;
#endif

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

#define EV_WATCHER_LIST(type)			\
  EV_WATCHER (type)				\
  struct ev_watcher_list *next; /* private */

#define EV_WATCHER_TIME(type)			\
  EV_WATCHER (type)				\
  ev_tstamp at;     /* private */

 首先看一下宏定义EV_WATCHER(type),active表示这个监视器是否处于激活状态, pending用于表示该监视器在触发过的相同优先级下所有监视器数组的索引下标。因为相同优先级的监视器可能有很多,所以我们需要一个结构保存这样的一组数据,于是就需要索引/下标进行区分。priority表示监视器的优先级,其值可以从-2~2,共5个级别。其中2是最高级别,-2是最低级别。级别高的监视器会优先于级别低的监视器执行,cb是事件响应函数指针,data则是用于保存用户自定义的数据。示例中创建了两个事件监视器:

ev_io stdin_watcher;
ev_timer timeout_watcher;

以及对应的回调函数:

// ev_io事件的回调函数,当有输入流stdin时,调用函数
static void stdin_cb (EV_P_ ev_io *w, int revents)
{
puts ("stdin ready");
//对ev_io事件的监控不会自动停止,需要手动在需要的时候停止
ev_io_stop (EV_A_ w);
 
//整体的loop事件在所有监控停止时停止,也可以手动关闭全部的ev_run
ev_break (EV_A_ EVBREAK_ALL);
}
 
// 时间事件的自定义回调函数,可定时触发
static void timeout_cb (EV_P_ ev_timer *w, intrevents)
{
puts ("timeout");
//关闭最早的一个还在运行的ev_run
ev_break (EV_A_ EVBREAK_ONE);
}

随后通过ev_io_init初始化io事件监控,并绑定回调函数,下面详细分析下ev_io_init都做了些什么,ev_io的定义如下:

typedef struct ev_io
{
  EV_WATCHER_LIST (ev_io)

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

可以看到除了EV_WATCHER_LIST外,还定义了文件描述符fd和事件描述符events。ev_io_init(ev,cb,fd,events)的定义如下:

do 
{ 
ev_init ((ev), (cb)); 
ev_io_set ((ev),(fd),(events)); 
} while (0)

首先是ev_init((ev),(cb)):

#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)

前三行,将ev_io监视器ev的active、pending以及priority初始化为0,最后一行ev_set_cb ((ev), cb_)的定义为:

#define ev_cb_(ev) (ev)->cb /* rw */
...........................
...........................
# define ev_set_cb(ev,cb_) 
(ev_cb_ (ev) = (cb_), memmove 
(&((ev_watcher *)(ev))->cb, &ev_cb_ (ev), sizeof (ev_cb_ (ev))))

即ev得函数指针cb指向cb_。接下来看函数ev_io_set :

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

分别将文件描述符fd_和事件描述符events_赋值给对应的监视器ev的fd和events,这里使用了do{}while(0)循环,是linux底层变成经常用的一种方法,保证在调用时不出现第2行代码不执行的情况,例如 if cond xxxx;其中xxx为定义宏,此种情况若不加do{}while(0),会导致if条件下只执行代码的第一行,导致错误。

以上分析可以看到,ev_io_init的作用是将监听事件、回调函数和文件描述符绑定到对应的监视器ev,其中events设置了监控的触发条件,即监控文件fd上的可读还是可写亦或是其他事件。

继续往下看,ev_io_start (loop, &stdin_watcher);的源代码为:

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

ev_io_start的定义在ev.c中,其最终会编译到动态链接库中(大部分非宏定义的函数都会编译到动态链接库),故调用libev api时只需要头文件和lib.so库即可。通过宏定义可以得到EV_P_的实际值是struct ev_loop *loop,struct 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
  };

展开头文件可以得到其实际定义可以表示为:

struct ev_loop
{
ev_tstamp ev_rt_now;
#define ev_rt_now ((loop)->ev_rt_now)
#define VAR(name,decl) decl;
#define VARx(type,name) VAR(name, type name)

VARx(ev_tstamp, now_floor) /* last time we refreshed rt_time */
VARx(ev_tstamp, mn_now)    /* monotonic clock "now" */
VARx(ev_tstamp, rtmn_diff) /* difference realtime - monotonic time */

/* for reverse feeding of events */
VARx(W *, rfeeds)
VARx(int, rfeedmax)
VARx(int, rfeedcnt)

VAR (pendings, ANPENDING *pendings [NUMPRI])
VAR (pendingmax, int pendingmax [NUMPRI])
VAR (pendingcnt, int pendingcnt [NUMPRI])
VARx(int, pendingpri) /* highest priority currently pending */
VARx(ev_prepare, pending_w) /* dummy pending watcher */

VARx(ev_tstamp, io_blocktime)
VARx(ev_tstamp, timeout_blocktime)

VARx(int, backend)
VARx(int, activecnt) /* total number of active events ("refcount") */
VARx(EV_ATOMIC_T, loop_done)  /* signal by ev_break */

VARx(int, backend_fd)
VARx(ev_tstamp, backend_mintime) /* assumed typical timer resolution */
VAR (backend_modify, void (*backend_modify)(EV_P_ int fd, int oev, int nev))
VAR (backend_poll  , void (*backend_poll)(EV_P_ ev_tstamp timeout))

VARx(ANFD *, anfds)
VARx(int, anfdmax)

VAR (evpipe, int evpipe [2])
VARx(ev_io, pipe_w)
VARx(EV_ATOMIC_T, pipe_write_wanted)
VARx(EV_ATOMIC_T, pipe_write_skipped)

#if !defined(_WIN32) || EV_GENWRAP
VARx(pid_t, curpid)
#endif

VARx(char, postfork)  /* true if we need to recreate kernel state after fork */

#if EV_USE_SELECT || EV_GENWRAP
VARx(void *, vec_ri)
VARx(void *, vec_ro)
VARx(void *, vec_wi)
VARx(void *, vec_wo)
#if defined(_WIN32) || EV_GENWRAP
VARx(void *, vec_eo)
#endif
VARx(int, vec_max)
#endif

#if EV_USE_POLL || EV_GENWRAP
VARx(struct pollfd *, polls)
VARx(int, pollmax)
VARx(int, pollcnt)
VARx(int *, pollidxs) /* maps fds into structure indices */
VARx(int, pollidxmax)
#endif

#if EV_USE_EPOLL || EV_GENWRAP
VARx(struct epoll_event *, epoll_events)
VARx(int, epoll_eventmax)
VARx(int *, epoll_eperms)
VARx(int, epoll_epermcnt)
VARx(int, epoll_epermmax)
#endif

#if EV_USE_KQUEUE || EV_GENWRAP
VARx(pid_t, kqueue_fd_pid)
VARx(struct kevent *, kqueue_changes)
VARx(int, kqueue_changemax)
VARx(int, kqueue_changecnt)
VARx(struct kevent *, kqueue_events)
VARx(int, kqueue_eventmax)
#endif

#if EV_USE_PORT || EV_GENWRAP
VARx(struct port_event *, port_events)
VARx(int, port_eventmax)
#endif

#if EV_USE_IOCP || EV_GENWRAP
VARx(HANDLE, iocp)
#endif

VARx(int *, fdchanges)
VARx(int, fdchangemax)
VARx(int, fdchangecnt)

VARx(ANHE *, timers)
VARx(int, timermax)
VARx(int, timercnt)

#if EV_PERIODIC_ENABLE || EV_GENWRAP
VARx(ANHE *, periodics)
VARx(int, periodicmax)
VARx(int, periodiccnt)
#endif

#if EV_IDLE_ENABLE || EV_GENWRAP
VAR (idles, ev_idle **idles [NUMPRI])
VAR (idlemax, int idlemax [NUMPRI])
VAR (idlecnt, int idlecnt [NUMPRI])
#endif
VARx(int, idleall) /* total number */

VARx(struct ev_prepare **, prepares)
VARx(int, preparemax)
VARx(int, preparecnt)

VARx(struct ev_check **, checks)
VARx(int, checkmax)
VARx(int, checkcnt)

#if EV_FORK_ENABLE || EV_GENWRAP
VARx(struct ev_fork **, forks)
VARx(int, forkmax)
VARx(int, forkcnt)
#endif

#if EV_CLEANUP_ENABLE || EV_GENWRAP
VARx(struct ev_cleanup **, cleanups)
VARx(int, cleanupmax)
VARx(int, cleanupcnt)
#endif

#if EV_ASYNC_ENABLE || EV_GENWRAP
VARx(EV_ATOMIC_T, async_pending)
VARx(struct ev_async **, asyncs)
VARx(int, asyncmax)
VARx(int, asynccnt)
#endif

#if EV_USE_INOTIFY || EV_GENWRAP
VARx(int, fs_fd)
VARx(ev_io, fs_w)
VARx(char, fs_2625) /* whether we are running in linux 2.6.25 or newer */
VAR (fs_hash, ANFS fs_hash [EV_INOTIFY_HASHSIZE])
#endif

VARx(EV_ATOMIC_T, sig_pending)
#if EV_USE_SIGNALFD || EV_GENWRAP
VARx(int, sigfd)
VARx(ev_io, sigfd_w)
VARx(sigset_t, sigfd_set)
#endif

VARx(unsigned int, origflags) /* original loop flags */

#if EV_FEATURE_API || EV_GENWRAP
VARx(unsigned int, loop_count) /* total number of loop iterations/blocks */
VARx(unsigned int, loop_depth) /* #ev_run enters - #ev_run leaves */

VARx(void *, userdata)
/* C++ doesn't support the ev_loop_callback typedef here. stinks. */
VAR (release_cb, void (*release_cb)(EV_P) EV_THROW)
VAR (acquire_cb, void (*acquire_cb)(EV_P) EV_THROW)
VAR (invoke_cb , ev_loop_callback invoke_cb)
#endif

#undef VARx
#undef VAR
  };

通过两个宏定义VAR和VARX定义了很多变量。前面几行会有一些检测代码,略过。ev_start (EV_A_ (W)w, 1)定义为:

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

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

pri_adjust检测并调整监视器w的优先级在-2到2之间,随后将监视器的活动状态设定为激活,最后调用ev_ref(EV_A):

ev_ref (EV_P) EV_THROW
{
  ++activecnt;
}

使得(loop)->activecnt加1,即当前loop的活动监视器数+1。

回到ev_io_start函数,接下来调用array_needsize (ANFD, anfds, anfdmax, fd + 1, array_init_zero),在Libev中,如果某种结构的数组需要扩容,它使用array_needsize宏进行处理,比如:array_needsize (int, fdchanges, fdchangemax, fdchangecnt, EMPTY2);这就表示要将整型(int)数组fdchanges,由原来的fdchangemax个元素扩容为fdchangecnt,新扩容的内存空间使用EMPTR2进行初始化,即数组的大小为所有fd中的最大值,当然fd的值是不会太大的,所以不用担心容量太大,并且操作系统一般都是将最小的空闲描述符分配给用户,所以不需要担心数组中有大量的闲置节点。array_needsize宏定义如下:

#define array_needsize(type,base,cur,cnt,init)                           \
  if (expect_false ((cnt) > (cur)))                                      \
  {                                                                      \
    int ecb_unused ocur_ = (cur);                                        \
    (base) = (type *)array_realloc(sizeof(type), (base), &(cur), (cnt)); \
    init ((base) + (ocur_), (cur) - ocur_);                              \
  }

base是type类型的数组,当前有cur个元素,需要调整到cnt个元素,新扩充的内存空间使用init函数初始化。这里andfs的定义为

#define anfds ((loop)->anfds)

可见其实际上是loop的成员andfs。结构体ANFD的定义如下:

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;
  unsigned int egen;    /* generation counter to counter epoll bugs */
} ANFD;  /* 这里去掉了对epoll的判断和windows的IOCP*/

一个ANFD就表示对一个文件描述符的监控,重点看下WL head,WL的类型为ev_watcher_list *,回看文章开头监视器watcher的定义,EV_WATCHER_LIST为:

#define EV_WATCHER_LIST(type)			\
  EV_WATCHER (type)				\
  struct ev_watcher_list *next; /* private */

也就是说凡是开头定义为EV_WATCHER_LIST的watcher都包含了struct ev_watcher_list *next,当然ev_io也不例外,struct ev_watcher_list定义如下:

typedef struct ev_watcher_list
{
  EV_WATCHER_LIST (ev_watcher_list)
} ev_watcher_list;

可以看到ev_watcher_list是对EV_WATCHER_LIST做了“包装”后的结构体,是watcher基本结构的一个链表。一个ANFD就表示对一个文件描述符的监控,那么是对该文件描述的可读还是可写监控或者监控的动作是如何定义的,就是通过这个链表,把对该文件描述法的监控器都挂上去,这样就可以通过文件描述符找到了,也就是说一个文件描述符一对一地对应一个ANFD实例,但同一个描述符的监控可以有多个(当然一般也不会超3个),比如说读事件、写事件都会有监控,这样的话ANFD实例中的head指向一个对应文件描述符所有监控watcher组成的一个单向链表。anfds是ANFD实例组成的一个数组,下标通过文件描述符fd进行索引,可以达到O(1)的索引速度。接下来执行代码wlist_add (&anfds[fd].head, (WL)w);,wlist_add定义为:

inline_size void
wlist_add (WL *head, WL elem)
{
  elem->next = *head;
  *head = elem;
}

即对于一个因加入地文件watcher,将其最为表头,head指向其,当然如果之前已经有该文件描述fd的watcher,我们需要用elem->next= *head,把之前的watcher链表放在后面。

接下来执行fd_change (EV_A_ fd, w->events & EV__IOFDSET | EV_ANFD_REIFY);,fd_change定义如下:

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的数组里。该数组记录了anfds数组中fd对应的watcher监控条件可能被修改的文件描述符,并且在适当的时候调用系统的io复用机制修改系统的监控条件。正如官方代码注释中说的:make sure the external fd watch events are in-sync with the kernel/libev internal state 具体的anfd和fdchanges结构如下:

 ç»æå¾

fd_change与fd_reify相对应。前者将fd添加到一个fdchanges的数组中,后者则依次遍历这个数组中的fd上的watcher与anfds里面对饮的watcher进行对比,判断监控条件是否改变了,如果改变了则调用backend_modify也就是epoll_ctl等调整系统对该fd的监控。这个fdchanges数组的作用就在于此,他记录了anfds数组中的watcher监控条件可能被修改的文件描述符,并在适当的时候将调用系统的epoll_ctl或则其他文件复用机制修改系统监控的条件。

总结一下注册过程就是通过之前设置了监控条件IO watcher获得监控的文件描述符fd,找到其在anfds中对应的ANFD结构,将该watcher挂到该结构的head链上。由于对应该fd的监控条件有改动了,因此在fdchanges数组中记录下该fd,在后续的步骤中调用系统的接口修改对该fd监控的条件。

最后我们来看ev_run函数如何启动事件驱动,ev_run的内容很多,忽略其他部分,对主要内容进行分析,最主要部分是一个大的while循环,在循环中依次执行如下几个主要的函数。1)redify函数,首先轮询fdchanges数组,对于符合条件的fd,调用backend_modify函数加入相应的异步结构中(iocp、epoll、epoll和poll等),backend_modify根据不同的IO复用模型选择对应的XXX_modify函数。2)调用time_update,更新时间变量mn_now 和 ev_rt_now,调整select、poll或者epoll的阻塞时间。 3)调用backend_poll(select,poll或epoll_wait)等待事件发生,事件发生后,调用fd_event,根据触发的描述符fd找到loop->anfds中相应的ANFD结构,遍历ANFD中的监视器链表,根据触发的事件,调用ev_feed_event将触发的监视器添加到loop->pendings中。4)ev_invoke_pending按照优先级顺序遍历loop->pendings数组,调用其中每个监视器的回调函数。

同样,通过ev_timer_init初始化timer事件并绑定回调函数,参数3和4分别表示多少秒之后调用回调函数以及第一次触发回调后每多少秒再次重复触发回调。随后通过ev_io_start和ev_timer_start,将已经初始化好的watcher绑定到事件监听主循环ev_loop上。ev_io_stop的作用正好与ev_io_start想法,就是把watcher从ev_loop中去除。ev_break表示中断 loop,参数可以是 EVBREAK_ONE(执行完一个内部调用后返回)或EVBREAK_ALL(执行完所有)。

发布了10 篇原创文章 · 获赞 2 · 访问量 611

猜你喜欢

转载自blog.csdn.net/fangfanglovezhou/article/details/104923064