title: Node.js 源碼分析 - 原生模塊(C++模塊)的注冊
date: 2018-11-28 21:04:49
tags:
- Node.js
- Node.js 源碼分析
- 源碼分析
categories:
- Node.js 源碼分析
此文最初于四年前發(fā)布在個人站上的,現(xiàn)遷移至此重發(fā),原鏈接:https://laogen.site/nodejs/nodejs-src/register-builtin-modules/
《Node.js 源碼分析》 系列目錄頁:https://laogen.site/nodejs/nodejs-src/index/
上一篇提到 RegisterBuiltinModules()
注冊了原生 C++ 模塊沒有詳細(xì)展開丧荐,這里就從這個函數(shù)展開。
將 RegisterBuiltinModules() 層層展開
/* src/node.cc:3066 */
void RegisterBuiltinModules() {
#define V(modname) _register_##modname();
NODE_BUILTIN_MODULES(V)
#undef V
}
首先定義了一個宏 V
為 _register_##modname()
, 可以看出 V
展開后是一個函數(shù)調(diào)用類似這樣: _register_xx()
;
隨后埠巨,RegisterBuiltinModules()
實(shí)際是宏 NODE_BUILTIN_MODULES(V)
來實(shí)現(xiàn)的宏悦,我們看看它的定義:
/* src/node_internals.h:147 */
#define NODE_BUILTIN_MODULES(V) \
NODE_BUILTIN_STANDARD_MODULES(V)
// ...
進(jìn)一步查看 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)
這個宏定義中多次調(diào)用宏 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();
// ...
}
經(jīng)過層層的宏展開,我們看到 RegisterBuiltinModules()
的原貌无畔,就是調(diào)用了一些全局注冊函數(shù)啊楚,這樣就能理解了。
接下來浑彰,我們打算看看這些注冊函數(shù)是在哪里定義的恭理。 我全局搜索了整個代碼目錄,也沒找到這些函數(shù)中的任何一個郭变,看來又是通過宏定義的颜价。
那我們就挑一個原生模塊的源碼,來看看里面有沒有上面注冊函數(shù)的定義诉濒,我挑了模塊名為 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) {
// ...
}
// 這個初始化函數(shù)是每個原生模塊都會定義的,它的參數(shù)也是一致的
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
模塊先是定義了一些函數(shù)未荒,代碼最后一行是個宏調(diào)用专挪,這個宏把模塊名 os
和 Initialize
函數(shù)做為其參數(shù),我們找到它的定義如下:
/* src/node_internals.h:169 */
#define NODE_BUILTIN_MODULE_CONTEXT_AWARE(modname, regfunc) \
NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_BUILTIN)
又是一個宏定義片排,繼續(xù)跟下去:
/* 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)
完全展開了:
// 創(chuàng)建一個 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() 函數(shù)
void _register_os() {
node_module_register(&_module);
}
到此,我們就明白了 RegisterBuiltinModules()
函數(shù)中調(diào)用的 _register_os()
是在哪里定義的了率寡,隨后查看了所有原生模塊的代碼迫卢,最后一行都是以同樣的方式定義相應(yīng)的 _register_xx()
。
其中 node::node_module
類型就代表一個模塊的信息冶共。
所謂注冊 os
模塊實(shí)際是調(diào)用了 node_module_register(node_module *)
函數(shù)完成的乾蛤,我們繼續(xù)來看看 node_module_register()
函數(shù)和 node::node_module
:
模塊注冊實(shí)現(xiàn)
/* src/node.h:518*/
struct node_module {
int nm_version;
unsigned int nm_flags;
void* nm_dso_handle;
const char* nm_filename;
// 上例中 Initialize 函數(shù)被賦到 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);
}
}
到這里就清晰了每界, 所謂原生模塊的注冊,實(shí)際上就是將一個類型為 node::node_module
的模塊對象幻捏,添加到不同類別的全局鏈表中盆犁。
上述代碼中用3個全局鏈表:modlist_builtin
modlist_internal
modlist_linked
分別保存不同類型的模塊命咐,本文我們說的是 BUILTIN
類型的篡九,也就是第一個。
我把這幾個鏈表的定義位置發(fā)出來:
/* src/node.cc:175 */
static node_module* modlist_builtin; // 我們現(xiàn)在只關(guān)注 builtin 模塊
static node_module* modlist_internal;
static node_module* modlist_linked;
static node_module* modlist_addon;
小結(jié)
這個原生模塊的注冊過程就寫到這里醋奠,邏輯還是比較簡單的榛臼,只是連續(xù)的宏定義讓代碼不那么直觀。
原生模塊加載寫完后窜司,接下來沛善,會繼續(xù)寫原生模塊的加載篇。
Maslow (wangfugen@126.com)塞祈, laf.js 作者金刁。
lafyun.com 開源云開發(fā)平臺,前端變?nèi)珬R樾剑瑹o需服務(wù)端尤蛮。