abbshr / abbshr.github.io

人们往往接受流行,不是因为想要与众不同,而是因为害怕与众不同
http://digitalpie.cf
444 stars 44 forks source link

libuv源码初探 #26

Open abbshr opened 9 years ago

abbshr commented 9 years ago

关于libuv,不必做过多介绍。从Node.js到Luvit,Rust,pyuv等无不体现libuv应用之广泛。

仍然是因“How does it work”的问题促使我去github扒源码。要说现在没有libuv的文档或者参考资料吗?当然有,只不过他们都没有进一步深入介绍,每份参考资料都仅仅停留在API基本使用上,并且还不完整,就像隔着一层窗户纸一样,这难以让人不产生一探究竟的冲动啊。

我想起之前写的那份“阅读Node.js源码”,在写那篇日志前我主要阅读的是多数调用V8相关的源码,而对libuv貌似并没有提及。Node.js的入口源码node.cc中有一段展示Node中是如何使用libuv的最最核心的代码,也是事件循环的起点:

// src/node.cc
// 源码 3626行
    {
      Context::Scope context_scope(env->context());
      bool more;
      do {
        // 启动event-loop
        more = uv_run(env->event_loop(), UV_RUN_ONCE);
        if (more == false) {
          EmitBeforeExit(env);

          // Emit `beforeExit` if the loop became alive either after emitting
          // event, or after running some callbacks.
          // 检测event-loop是否需要继续存活
          more = uv_loop_alive(env->event_loop());
          if (uv_run(env->event_loop(), UV_RUN_NOWAIT) != 0)
            more = true;
        }
      } while (more == true);
      code = EmitExit(env);
      RunAtExit(env);
    }
    env->Dispose();
    env = NULL;
  }

  CHECK_NE(node_isolate, NULL);
  node_isolate->Dispose();
  node_isolate = NULL;
  V8::Dispose();

  delete[] exec_argv;
  exec_argv = NULL;

  return code;
}

在Node的启动中有三处调用了libuv的API,我们得去libuv的源码中找找看,那些参数代表什么。官方文档中提到uv.h包含了libuv API,所以应该从uv.h入手:

// includes/uv.h
// 源码 302行
// 这里声明了uv_run函数.注释给的很清楚,这个函数的功能是启动事件循环,同时有三种不同启动模式可供选择:
   UV_RUN_DEFAULT: 默认的循环模式,将会不断重复这个循环,直到"循环引用计数器(ref)"减为0.
   UV_RUN_ONCE: 在没有待处理事件时uv_run将会阻塞,但这个事件循环仅仅轮询一次新触发的事件.当没有活跃的监听器或请求,函数返回0,否则返回非0值,表示还有更多待处理事件.
   UV_RUN_NOWAIT: 即使没有待处理事件也不会阻塞uv_run函数,其余和UV_RUN_ONCE相同.
/*
 * This function runs the event loop. It will act differently depending on the
 * specified mode:
 *  - UV_RUN_DEFAULT: Runs the event loop until the reference count drops to
 *    zero. Always returns zero.
 *  - UV_RUN_ONCE: Poll for new events once. Note that this function blocks if
 *    there are no pending events. Returns zero when done (no active handles
 *    or requests left), or non-zero if more events are expected (meaning you
 *    should run the event loop again sometime in the future).
 *  - UV_RUN_NOWAIT: Poll for new events once but don't block if there are no
 *    pending events. Returns zero when done (no active handles
 *    or requests left), or non-zero if more events are expected (meaning you
 *    should run the event loop again sometime in the future).
 */
UV_EXTERN int uv_run(uv_loop_t*, uv_run_mode mode);

uv_run函数的实现在src/unix/core.c中:

// src/unix/core.c
// 源码 304行

int uv_run(uv_loop_t* loop, uv_run_mode mode) {
  int timeout;
  int r;

  // 在进入函数时首先检查该循环是否存货,
  // 言外之意:是否有活跃的监听器或请求或者是否有关闭回调函数
  // 这是uv__loop_alive函数的作用
  r = uv__loop_alive(loop);
  if (!r)
    uv__update_time(loop);

  // 如果事件循环是alive的,就进入这个while循环
  while (r != 0 && loop->stop_flag == 0) {
    // 这里表示每次while循环都是一个Tick
    // 该宏相当于注释
    UV_TICK_START(loop, mode);

    uv__update_time(loop);
    uv__run_timers(loop);
    uv__run_pending(loop);
    uv__run_idle(loop);
    uv__run_prepare(loop);

    timeout = 0;
    if ((mode & UV_RUN_NOWAIT) == 0)
      timeout = uv_backend_timeout(loop);

    uv__io_poll(loop, timeout);
    uv__run_check(loop);
    uv__run_closing_handles(loop);

    if (mode == UV_RUN_ONCE) {
      /* UV_RUN_ONCE implies forward progess: at least one callback must have
       * been invoked when it returns. uv__io_poll() can return without doing
       * I/O (meaning: no callbacks) when its timeout expires - which means we
       * have pending timers that satisfy the forward progress constraint.
       *
       * UV_RUN_NOWAIT makes no guarantees about progress so it's omitted from
       * the check.
       */
      uv__update_time(loop);
      uv__run_timers(loop);
    }

    // 一次Tick结束之前再次检查事件循环是否仍是alive的
    r = uv__loop_alive(loop);
    // 下面同样是个注释,表示本次Tick结束了
    UV_TICK_STOP(loop, mode);

    if (mode & (UV_RUN_ONCE | UV_RUN_NOWAIT))
      break;
  }

  /* The if statement lets gcc compile it to a conditional store. Avoids
   * dirtying a cache line.
   */
  if (loop->stop_flag != 0)
    loop->stop_flag = 0;

  return r;
}

上面所用的uv__loop_alive函数定义在同一文件的292行:

// src/unix/core.c
// 源码 292行

// 如何判断一个事件循环是否时是存活的?
// 这个函数简洁明了
static int uv__loop_alive(const uv_loop_t* loop) {
  return uv__has_active_handles(loop) ||
         uv__has_active_reqs(loop) ||
         loop->closing_handles != NULL;
}

uv__has_active_handles函数和uv__has_active_reqs函数的实现在src/uv-common.h中:

// src/uv-common.h
//源码 114行

// 原来这两个'函数'是两个宏定义
// 分别定义了active_reqs和active_handles的各种操作
#define uv__has_active_reqs(loop)                                             \
  (QUEUE_EMPTY(&(loop)->active_reqs) == 0)

#define uv__req_register(loop, req)                                           \
  do {                                                                        \
    QUEUE_INSERT_TAIL(&(loop)->active_reqs, &(req)->active_queue);            \
  }                                                                           \
  while (0)

#define uv__req_unregister(loop, req)                                         \
  do {                                                                        \
    assert(uv__has_active_reqs(loop));                                        \
    QUEUE_REMOVE(&(req)->active_queue);                                       \
  }                                                                           \
  while (0)

#define uv__has_active_handles(loop)                                          \
  ((loop)->active_handles > 0)

#define uv__active_handle_add(h)                                              \
  do {                                                                        \
    (h)->loop->active_handles++;                                              \
  }                                                                           \
  while (0)

#define uv__active_handle_rm(h)                                               \
  do {                                                                        \
    (h)->loop->active_handles--;                                              \
  }                                                                           \
  while (0)

下面是循环引用计数器的部分代码:

// src/uv-common.c
// 源码 374行

void uv_ref(uv_handle_t* handle) {
  uv__handle_ref(handle);
}

void uv_unref(uv_handle_t* handle) {
  uv__handle_unref(handle);
}

int uv_has_ref(const uv_handle_t* handle) {
  return uv__has_ref(handle);
}

具体仍以宏定义形式实现的:

// src/uv-common.h
// 源码 169行

#define uv__handle_ref(h)                                                     \
  do {                                                                        \
    if (((h)->flags & UV__HANDLE_REF) != 0) break;                            \
    (h)->flags |= UV__HANDLE_REF;                                             \
    if (((h)->flags & UV__HANDLE_CLOSING) != 0) break;                        \
    if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_add(h);      \
  }                                                                           \
  while (0)

#define uv__handle_unref(h)                                                   \
  do {                                                                        \
    if (((h)->flags & UV__HANDLE_REF) == 0) break;                            \
    (h)->flags &= ~UV__HANDLE_REF;                                            \
    if (((h)->flags & UV__HANDLE_CLOSING) != 0) break;                        \
    if (((h)->flags & UV__HANDLE_ACTIVE) != 0) uv__active_handle_rm(h);       \
  }                                                                           \
  while (0)

#define uv__has_ref(h)                                                        \
  (((h)->flags & UV__HANDLE_REF) != 0)

用于退出事件循环的函数是这样定义的:

// src/uv-common.c
// 源码 389行

// 仅仅把事件循环的stop_flag置1
void uv_stop(uv_loop_t* loop) {
  loop->stop_flag = 1;
}

Pending!

以上这些仅仅是libuv的一个初探,至于说能让不理解Node.js的事件循环运作原理的人大彻大悟,那是说笑了.因为在uv_run中调用的好多其他重要函数这里还没做介绍解释.

下篇日志仍会从uv_run函数开始,详细而有条理的剖析事件循环的初始化,事件触发及监视器的工作流程,事件循环的退出.

Reference

Source Code An Introduction to libuv libuv-dox