2021 iThome 鐵人賽

DAY 22

從C到JS的同步非同步探索系列 第 22

[Day 22] Node Event loop 1


昨天, 我們知道了 JS 層藉由 V8 引用 C++ 層, C++ 層又利用 AIO (非同步IO) 註冊事件, 接著我們就來看看負責處理事件的 libuv 在做甚麼吧。

Event loop 的啟動

來到 C++ 層的運行起點

int main(int argc, char* argv[]) {
#if defined(__POSIX__) && defined(NODE_SHARED_MODE)
  // In node::PlatformInit(), we squash all signal handlers for non-shared lib
  // build. In order to run test cases against shared lib build, we also need
  // to do the same thing for shared lib build here, but only for SIGPIPE for
  // now. If node::PlatformInit() is moved to here, then this section could be
  // removed.
    struct sigaction act;
    memset(&act, 0, sizeof(act));
    act.sa_handler = SIG_IGN;
    sigaction(SIGPIPE, &act, nullptr);

#if defined(__linux__)
  node::per_process::linux_at_secure = getauxval(AT_SECURE);
  // Disable stdio buffering, it interacts poorly with printf()
  // calls elsewhere in the program (e.g., any logging from V8.)
  setvbuf(stdout, nullptr, _IONBF, 0);
  setvbuf(stderr, nullptr, _IONBF, 0);
  return node::Start(argc, argv);

重點其實就是最後的 node::Start(argc, argv);

int Start(int argc, char** argv) {
  InitializationResult result = InitializeOncePerProcess(argc, argv);
  if (result.early_return) {
    return result.exit_code;

    Isolate::CreateParams params;
    const std::vector<size_t>* indices = nullptr;
    const EnvSerializeInfo* env_info = nullptr;
    bool use_node_snapshot =
    if (use_node_snapshot) {
      v8::StartupData* blob = NodeMainInstance::GetEmbeddedSnapshotBlob();
      if (blob != nullptr) {
        params.snapshot_blob = blob;
        indices = NodeMainInstance::GetIsolateDataIndices();
        env_info = NodeMainInstance::GetEnvSerializeInfo();
    uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME);

    NodeMainInstance main_instance(&params,
    result.exit_code = main_instance.Run(env_info);

  return result.exit_code;

可以看到實體化了一個 NodeMainInstance , 接著調用了該物件的 Run

void NodeMainInstance::Run(int* exit_code, Environment* env) {
  if (*exit_code == 0) {
    LoadEnvironment(env, StartExecutionCallback{});

    *exit_code = SpinEventLoop(env).FromMaybe(1);


  // TODO(addaleax): Neither NODE_SHARED_MODE nor HAVE_INSPECTOR really
  // make sense here.
#if HAVE_INSPECTOR && defined(__POSIX__) && !defined(NODE_SHARED_MODE)
  struct sigaction act;
  memset(&act, 0, sizeof(act));
  for (unsigned nr = 1; nr < kMaxSignal; nr += 1) {
    if (nr == SIGKILL || nr == SIGSTOP || nr == SIGPROF)
    act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL;
    CHECK_EQ(0, sigaction(nr, &act, nullptr));

#if defined(LEAK_SANITIZER)

找到 SpinEventLoop 這就是 Event loop (libuv) 的創建入口

補充 : LoadEnvironment(env, StartExecutionCallback{}); 這聚會往上創建 JS 層, 但基於主體, 不深入細講。

所以可以視為, 這個函數裡面的兩句, 分別往上建立 JS 層, 和往下建立 libuv 層

// Runs the main loop for a given Environment. This roughly performs the
// following steps:
// 1. Call uv_run() on the event loop until it is drained.
// 2. Call platform->DrainTasks() on the associated platform/isolate.
//   3. If the event loop is alive again, go to Step 1.
// 4. Call EmitProcessBeforeExit().
//   5. If the event loop is alive again, go to Step 1.
// 6. Call EmitProcessExit() and forward the return value.
// If at any point node::Stop() is called, the function will attempt to return
// as soon as possible, returning an empty `Maybe`.
// This function only works if `env` has an associated `MultiIsolatePlatform`.
NODE_EXTERN v8::Maybe<int> SpinEventLoop(Environment* env);

查看註解可以得知其會運行 uv_run() method , 而這就是 libuv 的核心循環。

而這句函式, 就是從 C++ 層走入 libuv 的入口。


今天從 C++ 層調用了外部方法準備運行 uv_run() , 這據說是 libuv 的核心循環, 明天一起來看看吧 !

明天見 !

[Day 21] Node 註冊事件 2
[Day 23] Node Event loop 2