Node.js 源码分析 - 原生模块(C++模块)的注册
2022/2/20 8:26:17
本文主要是介绍Node.js 源码分析 - 原生模块(C++模块)的注册,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
title: Node.js 源码分析 - 原生模块(C++模块)的注册
date: 2018-11-28 21:04:49
tags:
- Node.js
- Node.js 源码分析
- 源码分析
categories:
- Node.js 源码分析
此文最初于四年前发布在个人站上的,现迁移至此重发,原链接:https://laogen.site/nodejs/nodejs-src/register-builtin-modules/
《Node.js 源码分析》 系列目录页:https://laogen.site/nodejs/nodejs-src/index/
上一篇提到 RegisterBuiltinModules()
注册了原生 C++ 模块没有详细展开,这里就从这个函数展开。
将 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 node NODE_BUILTIN_MODULE_CONTEXT_AWARE(os, node::os::Initialize)
这个 os
模块先是定义了一些函数,代码最后一行是个宏调用,这个宏把模块名 os
和 Initialize
函数做为其参数,我们找到它的定义如下:
/* 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 对象 _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
:
模块注册实现
/* 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;
小结
这个原生模块的注册过程就写到这里,逻辑还是比较简单的,只是连续的宏定义让代码不那么直观。
原生模块加载写完后,接下来,会继续写原生模块的加载篇。
Maslow (wangfugen@126.com), laf.js 作者。
这篇关于Node.js 源码分析 - 原生模块(C++模块)的注册的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-16Vue3资料:新手入门必读教程
- 2024-11-16Vue3资料:新手入门全面指南
- 2024-11-16Vue资料:新手入门完全指南
- 2024-11-16Vue项目实战:新手入门指南
- 2024-11-16React Hooks之useEffect案例详解
- 2024-11-16useRef案例详解:React中的useRef使用教程
- 2024-11-16React Hooks之useState案例详解
- 2024-11-16Vue入门指南:从零开始搭建第一个Vue项目
- 2024-11-16Vue3学习:新手入门教程与实践指南
- 2024-11-16Vue3学习:从入门到初级实战教程