title: Node.js 源码剖析 - 原生模块(C++模块)的注册
date: 2018-11-28 21:04:49
tags:

- Node.js- Node.js 源码剖析- 源码剖析

categories:

- Node.js 源码剖析

此文最后于四年前公布在集体站上的,现迁徙至此重发,原链接:https://laogen.site/nodejs/no...
《Node.js 源码剖析》 系列目录页:https://laogen.site/nodejs/no...

上一篇提到 RegisterBuiltinModules() 注册了原生 C++ 模块没有具体开展,这里就从这个函数开展。

<!-- more -->

将 RegisterBuiltinModules() 层层开展

/* src/node.cc:3066 */void RegisterBuiltinModules() {#define V(modname) _register_##modname();  NODE_BUILTIN_MODULES(V)#undef V}

首先定义了一个宏 V_register_##modname(), 能够看出 V 开展后是一个函数调用相似这样: _register_xx();

随后,RegisterBuiltinModules() 理论是宏 NODE_BUILTIN_MODULES(V) 来实现的,咱们看看它的定义:

/* src/node_internals.h:147 */#define NODE_BUILTIN_MODULES(V)    \  NODE_BUILTIN_STANDARD_MODULES(V)// ...

进一步查看 NODE_BUILTIN_STANDARD_MODULES(V) 的定义:

/* src/node_internals.h:106 */#define NODE_BUILTIN_STANDARD_MODULES(V)  \    V(async_wrap)       \    V(buffer)           \    V(cares_wrap)       \    V(config)           \    V(contextify)       \    V(domain)           \    V(fs)               \    V(fs_event_wrap)    \    V(heap_utils)       \    V(http2)            \    V(http_parser)      \    V(inspector)        \    V(js_stream)        \    V(messaging)        \    V(module_wrap)      \    V(options)          \    V(os)               \    V(performance)      \    V(pipe_wrap)        \    V(process_wrap)     \    V(serdes)           \    V(signal_wrap)      \    V(spawn_sync)       \    V(stream_pipe)      \    V(stream_wrap)      \    V(string_decoder)   \    V(symbols)          \    V(tcp_wrap)         \    V(timer_wrap)       \    V(trace_events)     \    V(tty_wrap)         \    V(types)            \    V(udp_wrap)         \    V(url)              \    V(util)             \    V(uv)               \    V(v8)               \    V(worker)           \    V(zlib)

这个宏定义中屡次调用宏 V,还记得这个宏吗,在下面定义的:#define V(modname) _register_##modname();,那咱们把它开展后就是:

/* src/node_internals.h:106 */#define NODE_BUILTIN_STANDARD_MODULES(V)  \    _register_async_wrap();    _register_buffer();    _register_cares_wrap();    _register_config();    _register_contextify();    _register_domain();    _register_fs();    _register_fs_event_wrap();    _register_heap_utils();    _register_http2();    _register_http_parser();    _register_inspector();    _register_js_stream();    _register_messaging();    _register_module_wrap();    _register_options();    _register_os();    _register_performance();    _register_pipe_wrap();    _register_process_wrap();    _register_serdes();    _register_signal_wrap();    _register_spawn_sync();    _register_stream_pipe();    _register_stream_wrap();    _register_string_decoder();    _register_symbols();    _register_tcp_wrap();    _register_timer_wrap();    _register_trace_events();    _register_tty_wrap();    _register_types();    _register_udp_wrap();    _register_url();    _register_util();    _register_uv();    _register_v8();    _register_worker();    _register_zlib();

最终,RegisterBuiltinModules() 开展后大略是这样的:

void RegisterBuiltinModules() {  _register_async_wrap();  _register_buffer();  // ...  _register_os();  // ...}

通过层层的宏开展,咱们看到 RegisterBuiltinModules() 的原貌,就是调用了一些全局注册函数,这样就能了解了。

接下来,咱们打算看看这些注册函数是在哪里定义的。 我全局搜寻了整个代码目录,也没找到这些函数中的任何一个,看来又是通过宏定义的。

那咱们就挑一个原生模块的源码,来看看外面有没有下面注册函数的定义,我挑了模块名为 os 的模块,它的源码位于 src/node_os.cc

查看一个原生模块的源码

/* src/node_os.cc */namespace node {namespace os {// ...static void GetHostname(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetOSType(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetOSRelease(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetCPUInfo(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetFreeMemory(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetTotalMemory(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetUptime(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetLoadAvg(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetInterfaceAddresses(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetHomeDirectory(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {  // ...}static void SetPriority(const FunctionCallbackInfo<Value>& args) {  // ...}static void GetPriority(const FunctionCallbackInfo<Value>& args) {  // ...}// 这个初始化函数是每个原生模块都会定义的,它的参数也是统一的void Initialize(Local<Object> target,                Local<Value> unused,                Local<Context> context) {  Environment* env = Environment::GetCurrent(context);  env->SetMethod(target, "getHostname", GetHostname);  env->SetMethod(target, "getLoadAvg", GetLoadAvg);  env->SetMethod(target, "getUptime", GetUptime);  env->SetMethod(target, "getTotalMem", GetTotalMemory);  env->SetMethod(target, "getFreeMem", GetFreeMemory);  env->SetMethod(target, "getCPUs", GetCPUInfo);  env->SetMethod(target, "getOSType", GetOSType);  env->SetMethod(target, "getOSRelease", GetOSRelease);  env->SetMethod(target, "getInterfaceAddresses", GetInterfaceAddresses);  env->SetMethod(target, "getHomeDirectory", GetHomeDirectory);  env->SetMethod(target, "getUserInfo", GetUserInfo);  env->SetMethod(target, "setPriority", SetPriority);  env->SetMethod(target, "getPriority", GetPriority);  target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "isBigEndian"),              Boolean::New(env->isolate(), IsBigEndian()));}}  // namespace os}  // namespace nodeNODE_BUILTIN_MODULE_CONTEXT_AWARE(os, node::os::Initialize)

这个 os 模块先是定义了一些函数,代码最初一行是个宏调用,这个宏把模块名 osInitialize 函数做为其参数,咱们找到它的定义如下:

/* src/node_internals.h:169 */#define NODE_BUILTIN_MODULE_CONTEXT_AWARE(modname, regfunc)   \  NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_BUILTIN)

又是一个宏定义,持续跟上来:

/* src/node_internals.h:152*/#define NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \  static node::node_module _module = { \    NODE_MODULE_VERSION,    \    flags,      \    nullptr,    \    __FILE__,   \    nullptr,    \    (node::addon_context_register_func) (regfunc),  \    NODE_STRINGIFY(modname),    \    priv,   \    nullptr \  };    \  void _register_ ## modname() {    \    node_module_register(&_module); \  }

这个宏的定义里如同看到了咱们要找的代码,咱们在这里就能够把 NODE_BUILTIN_MODULE_CONTEXT_AWARE(os, node::os::Initialize) 齐全开展了:

// 创立一个 node_module 对象 _modulestatic node::node_module _module = {    NODE_MODULE_VERSION,    NM_F_BUILTIN,    nullptr,    __FILE__,    nullptr,    (node::addon_context_register_func) (node::os::Initialize),      NODE_STRINGIFY(os),    nullptr,    nullptr};// 定义咱们要找的 _register_os() 函数void _register_os() {      node_module_register(&_module); }

到此,咱们就明确了 RegisterBuiltinModules() 函数中调用的 _register_os() 是在哪里定义的了,随后查看了所有原生模块的代码,最初一行都是以同样的形式定义相应的 _register_xx()

其中 node::node_module 类型就代表一个模块的信息。

所谓注册 os 模块理论是调用了 node_module_register(node_module *) 函数实现的,咱们持续来看看 node_module_register() 函数和 node::node_module

模块注册实现

/* src/node.h:518*/struct node_module {  int nm_version;  unsigned int nm_flags;  void* nm_dso_handle;  const char* nm_filename;  // 上例中 Initialize 函数被赋到 nm_register_func 里  node::addon_register_func nm_register_func;    node::addon_context_register_func nm_context_register_func;  const char* nm_modname; // 模块的名字  void* nm_priv;  struct node_module* nm_link;  };
/* src/node.cc:1094 */extern "C" void node_module_register(void* m) {  struct node_module* mp = reinterpret_cast<struct node_module*>(m);  if (mp->nm_flags & NM_F_BUILTIN) {    // 链表操作    mp->nm_link = modlist_builtin;    modlist_builtin = mp;  } else if (mp->nm_flags & NM_F_INTERNAL) {    // 链表操作    mp->nm_link = modlist_internal;    modlist_internal = mp;  } else if (!node_is_initialized) {    // "Linked" modules are included as part of the node project.    // Like builtins they are registered *before* node::Init runs.    mp->nm_flags = NM_F_LINKED;    mp->nm_link = modlist_linked;    modlist_linked = mp;  } else {    uv_key_set(&thread_local_modpending, mp);  }}

到这里就清晰了, 所谓原生模块的注册,实际上就是将一个类型为 node::node_module 的模块对象,增加到不同类别的全局链表中。

上述代码中用3个全局链表:modlist_builtin modlist_internal modlist_linked 别离保留不同类型的模块,本文咱们说的是 BUILTIN 类型的,也就是第一个。

我把这几个链表的定义地位收回来:

/* src/node.cc:175 */static node_module* modlist_builtin;   // 咱们当初只关注 builtin 模块static node_module* modlist_internal;static node_module* modlist_linked;static node_module* modlist_addon;

小结

这个原生模块的注册过程就写到这里,逻辑还是比较简单的,只是间断的宏定义让代码不那么直观。

原生模块加载写完后,接下来,会持续写原生模块的加载篇。


By Maslow (wangfugen@126.com), laf.js 作者。

lafyun.com 开源云开发平台,前端变全栈,无需服务端。