关于node.js:Nodejs-学习笔记一

39次阅读

共计 5437 个字符,预计需要花费 14 分钟才能阅读完成。

1. 先从 Nodejs 的入口谈起,一下采纳的是 nodejs 6.9.4 版本。
nodejs 启动的入口文件 src/node_main.cc

// UNIX
int main(int argc, char *argv[]) {// 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 办法,此办法定义在 src/node.cc 文件中。

int Start(int argc, char** argv) {PlatformInit();

  CHECK_GT(argc, 0);

  // Hack around with the argv pointer. Used for process.title = "blah".
  argv = uv_setup_args(argc, argv);

  // This needs to run *before* V8::Initialize().  The const_cast is not
  // optional, in case you're wondering.
  int exec_argc;
  const char** exec_argv;
  Init(&argc, const_cast<const char**>(argv), &exec_argc, &exec_argv);

#if HAVE_OPENSSL
#ifdef NODE_FIPS_MODE
  // In the case of FIPS builds we should make sure
  // the random source is properly initialized first.
  OPENSSL_init();
#endif  // NODE_FIPS_MODE
  // V8 on Windows doesn't have a good source of entropy. Seed it from
  // OpenSSL's pool.
  V8::SetEntropySource(crypto::EntropySource);
#endif

  v8_platform.Initialize(v8_thread_pool_size);
  V8::Initialize();

  int exit_code = 1;
  {
    NodeInstanceData instance_data(NodeInstanceType::MAIN,
                                   uv_default_loop(),
                                   argc,
                                   const_cast<const char**>(argv),
                                   exec_argc,
                                   exec_argv,
                                   use_debug_agent);
    StartNodeInstance(&instance_data);
    exit_code = instance_data.exit_code();}
  V8::Dispose();

  v8_platform.Dispose();

  delete[] exec_argv;
  exec_argv = nullptr;

  return exit_code;
}

此办法开端处调用了同一个文件中的 StartNodeInstance 办法,传入一个 NodeInstanceData(node_internals.h 中定义)实例,在这个办法中调用了 LoadEnvironment 办法,LoadEnvironment 办法有如下代码:

  // Execute the lib/internal/bootstrap_node.js file which was included as a
  // static C string in node_natives.h by node_js2c.
  // 'internal_bootstrap_node_native' is the string containing that source code.
  Local<String> script_name = FIXED_ONE_BYTE_STRING(env->isolate(),
                                                    "bootstrap_node.js");
  Local<Value> f_value = ExecuteString(env, MainSource(env), script_name);
  if (try_catch.HasCaught())  {ReportException(env, try_catch);
    exit(10);
  }
  // The bootstrap_node.js file returns a function 'f'
  CHECK(f_value->IsFunction());
  Local<Function> f = Local<Function>::Cast(f_value);

  // Add a reference to the global object
  Local<Object> global = env->context()->Global();

#if defined HAVE_DTRACE || defined HAVE_ETW
  InitDTrace(env, global);
#endif

#if defined HAVE_LTTNG
  InitLTTNG(env, global);
#endif

#if defined HAVE_PERFCTR
  InitPerfCounters(env, global);
#endif

  // Enable handling of uncaught exceptions
  // (FatalException(), break on uncaught exception in debugger)
  //
  // This is not strictly necessary since it's almost impossible
  // to attach the debugger fast enought to break on exception
  // thrown during process startup.
  try_catch.SetVerbose(true);

  env->SetMethod(env->process_object(), "_rawDebug", RawDebug);

  // Expose the global object as a property on itself
  // (Allows you to set stuff on `global` from anywhere in JavaScript.)
  global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global);

  // Now we call 'f' with the 'process' variable that we've built up with
  // all our bindings. Inside bootstrap_node.js and internal/process we'll
  // take care of assigning things to their places.

  // We start the process this way in order to be more modular. Developers
  // who do not like how bootstrap_node.js sets up the module system but do
  // like Node's I/O bindings may want to replace'f' with their own function.
  Local<Value> arg = env->process_object();
  f->Call(Null(env->isolate()), 1, &arg);

能够看到,这个 node 通过执行 /lib/internal/bootstrap_node.js 进行初始化,ExecuteString 之后失去的是 bootstrap_node 的一个对象,将其类型转换成 Function,在最初执行之 f ->Call(Null(env->isolate()), 1, &arg); 执行的时候传入的 &arg 就是 process 对象的援用。而 process 对象的设置是在雷同文件的 SetupProcessObject 函数中设置的,在这个调用过程中在 process 对象上设置了除 bind 和 dlopen 外的简直所有办法和对象。bootstrap_node.js 执行初始化的就是 Nodejs 的 module 模块零碎。

2.Node 启动后会首先加载其外围模块(既纯用 C ++ 编写的 Nodejs 自带模块)。而在 js 中援用这些模块都是应用 process.binding 函数进行援用,在 process 的初始化函数 SetupProcessObject 中能够看到如下代码:

 env->SetMethod(process, "binding", Binding);

也就是说理论应用的是 node.cc 文件中的 Binding 函数,如下:

  node_module* mod = get_builtin_module(*module_v);
  if (mod != nullptr) {exports = Object::New(env->isolate());
    // Internal bindings don't have a"module" object, only exports.
    CHECK_EQ(mod->nm_register_func, nullptr);
    CHECK_NE(mod->nm_context_register_func, nullptr);
    Local<Value> unused = Undefined(env->isolate());
    mod->nm_context_register_func(exports, unused,
      env->context(), mod->nm_priv);
    cache->Set(module, exports);
  } else if (!strcmp(*module_v, "constants")) {exports = Object::New(env->isolate());
    DefineConstants(env->isolate(), exports);
    cache->Set(module, exports);
  } else if (!strcmp(*module_v, "natives")) {exports = Object::New(env->isolate());
    DefineJavaScript(env, exports);
    cache->Set(module, exports);
  } else {char errmsg[1024];
    snprintf(errmsg,
             sizeof(errmsg),
             "No such module: %s",
             *module_v);
    return env->ThrowError(errmsg);
  }

  args.GetReturnValue().Set(exports);

能够看到,是调用 get_builtin_module 函数来获取编译好的 c ++ 模块的,看下 get_builtin_module 函数:

struct node_module* get_builtin_module(const char* name) {
  struct node_module* mp;

  for (mp = modlist_builtin; mp != nullptr; mp = mp->nm_link) {if (strcmp(mp->nm_modname, name) == 0)
      break;
  }

  CHECK(mp == nullptr || (mp->nm_flags & NM_F_BUILTIN) != 0);
  return (mp);
}

能够看到,这个函数在 modlist_builtin 构造中寻找对应输出名称的模块并返回。modlist_builtin 构造是在 node_module_register 函数(在 node.cc 文件中被定义)调用时被逐条注册的,而 node_module_register 调用的中央是在 node.h 中的宏定义

#define NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, priv, flags)    \
  extern "C" {                                                        \
    static node::node_module _module =                                \
    {                                                                 \
      NODE_MODULE_VERSION,                                            \
      flags,                                                          \
      NULL,                                                           \
      __FILE__,                                                       \
      NULL,                                                           \
      (node::addon_context_register_func) (regfunc),                  \
      NODE_STRINGIFY(modname),                                        \
      priv,                                                           \
      NULL                                                            \
    };                                                                \
    NODE_C_CTOR(_register_ ## modname) {                              \
      node_module_register(&_module);                                 \
    }                                                                 \
  }
#define NODE_MODULE_CONTEXT_AWARE_BUILTIN(modname, regfunc)           \
  NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, NM_F_BUILTIN)   \

在每个 src 目录下的 c ++ 模块都会在结尾处调用 NODE_MODULE_CONTEXT_AWARE_BUILTIN,也就是在模块被引入后就会将模块注册到 modlist_builtin 中去。供后续被其余模块援用。

正文完
 0