Node.js 源码分析 - 原生模块(C++模块)的注册

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

将 RegisterBuiltinModules() 层层展开

1
2
3
4
5
6
/* 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) 来实现的,我们看看它的定义:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* 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();,那我们把它展开后就是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* 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() 展开后大概是这样的:

1
2
3
4
5
6
7
void RegisterBuiltinModules() {
_register_async_wrap();
_register_buffer();
// ...
_register_os();
// ...
}

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

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

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

查看一个原生模块的源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/* 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 node

NODE_BUILTIN_MODULE_CONTEXT_AWARE(os, node::os::Initialize)

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

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

又是一个宏定义,继续跟下去:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/* 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) 完全展开了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 创建一个 node_module 对象 _module
static 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

模块注册实现

1
2
3
4
5
6
7
8
9
10
11
12
13
/* 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;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* 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 类型的,也就是第一个。

我把这几个链表的定义位置发出来:

1
2
3
4
5
/* 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;

小结

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

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