当前位置: 首页 > 后端技术 > Node.js

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

时间:2023-04-03 16:56:56 Node.js

标题:Node.js源码分析-原生模块(C++模块)的注册日期:2018-11-2821:04:49tags:-Node.js-Node.js源码分析-源码分析分类:-Node.js源码分析本文四年前发表于个人网站,现转载于此,原文链接:https://laogen.site/nodejs/no...《Node.js 源码分析》系列目录页:https://laogen.site/nodejs/no...上一篇文章提到RegisterBuiltinModules()注册原生C++模块,没有详细展开.这里我们就从这个功能来展开。逐层展开RegisterBuiltinModules()/*src/node.cc:3066*/voidRegisterBuiltinModules(){#defineV(modname)_register_##modname();NODE_BUILTIN_MODULES(V)#undefV}首先定义一个宏V为_register_##modname(),可以看出V展开后就是这样的函数调用:_register_xx();那么,RegisterBuiltinModules()实际上是由宏NODE_BUILTIN_MODULES(V)实现的,我们看一下它的定义:/*src/node_internals.h:147*/#defineNODE_BUILTIN_MODULES(V)\NODE_BUILTIN_STANDARD_MODULES(V)//...进一步查看NODE_BUILTIN_STANDARD_MODULES(V)的定义:/*src/node_internals.h:106*/#defineNODE_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(选项)\V(操作系统)\V(性能)\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。你还记得这个宏吗?上面定义了:#defineV(modname)_register_##modname();,那么我们展开后就是:/*src/node_internals.h:106*/#defineNODE_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();_注册er_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()扩展如下:voidRegisterBuiltinModules(){_register_async_wrap();_register_buffer();//..._register_os();//...}经过层层宏展开,我们看到了RegisterBuiltinModules()的原貌,它调用了一些全局的注册函数,这样我们就可以理解了。接下来,我们打算看看这些注册函数是在哪里定义的。我在全球范围内搜索了整个代码目录,但找不到任何这些函数,这些函数似乎又是通过宏定义的。那我们挑一个nativemodule的源码看看上面有没有注册函数的定义。我选择了一个名为os的模块,它的源代码位于src/node_os.cc:查看一个原生模块的源代码/*src/node_os.cc*/namespacenode{namespaceos{//...staticvoidGetHostname(constFunctionCallbackInfo&args){//...}staticvoidGetOSType(constFunctionCallbackInfo&args){//...}staticvoidGetOSRelease(constFunctionCallbackInfo&args){//...}staticvoidGetPUInfo(constFunctionCallbackInfo&args){//...}staticvoidGetFreeMemory(constFunctionCallbackInfo&args){//...}staticvoidGetTotalMemory(constFunctionCallbackInfo&args){//...}staticvoidGetUptime(constFunctionCallbackInfo&args){//...}staticvoidGetLoadAvg(constFunctionCallbackInfo&args){//...}staticvoidGetInterfaceAddresses(constFunctionCallbackInfo&args){//...}staticvoidGetHomeDirectory(constFunctionCallbackInfo&args){//...}staticvoidGetUserInfo(constFunctionCallbackInfo&args){//...}staticvoidSetPriority(constFunctionCallbackInfo&args){//...}staticvoidGetPriority(constFunctionCallbackInfo&args){//...}//这个初始化函数是每个native模块定义的,参数也是一致的voidInitialize(Localtarget,Local<值>未使用,本地<上下文>上下文){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(目标,"getCPUs",GetCPUInfo);env->SetMethod(target,"getOSType",GetOS类型);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()));}}//命名空间os}//命名空间nodeNODE_BUILTIN_MODULE_CONTEXT_AWARE(os,node::os::Initialize)这个os模块首先定义了一些函数。代码的最后一行是宏调用。该宏将模块名称os和Initialize函数作为其参数。我们找到它的定义如下:/*src/node_internals.h:169*/#defineNODE_BUILTIN_MODULE_CONTEXT_AWARE(modname,regfunc)\NODE_MODULE_CONTEXT_AWARE_CPP(modname,regfunc,nullptr,NM_F_BUILTIN)又是一个宏定义,继续往下看:/*src/node_internals.h:152*/#defineNODE_MODULE_CONTEXT_AWARE_CPP(modname,regfunc,priv,flags)\staticnode::node_module_module={\NODE_MODULE_VERSION,\flags,\nullptr,\__FILE__,\nullptr,\(node::addon_context_NOregister_func)(\modf),\priv,\nullptr\};\void_register_##modname(){\node_module_register(&_module);\}这个宏的定义好像看过我们要找的代码,这里我们可以把NODE_BUILTIN_MODULE_CONTEXT_AWARE(os,node::os::Initialize)完全展开://创建一个node_module对象_modulestaticnode::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(),然后我查看了所有native模块的代码,最后一行同样定义了对应的_register_xx()其中node::node_module类型表示一个模块的信息。os模块所谓的注册,其实是调用node_module_register(node_module*)函数完成的。我们继续看node_module_register()函数和node::node_module:模块注册实现/*src/node.h:518*/structnode_module{intnm_version;无符号整数nm_flags;void*nm_dso_handle;constchar*纳米文件名;//在上面的例子中,Initialize函数赋值给了nm_register_funcnode::addon_register_funcnm_register_func;node::addon_context_register_funcnm_context_register_func*模块名称;*nm_priv;结构节点模块*nm_link;};/*src/node.cc:1094*/extern"C"voidnode_module_register(void*m){structnode_module*mp=reinterpret_cast(m);if(mp->nm_flags&NM_F_BUILTIN){//链表操作mp->nm_link=modlist_builtin;modlist_builtin=mp;}elseif(mp->nm_flags&NM_F_INTERNAL){//链表操作mp->nm_link=modlist_internal;modlist_internal=mp;}elseif(!node_is_initialized){//包含“链接”模块作为节点项目的一部分。//与内置函数一样,它们在*before*node::Init运行之前注册。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_builtinmodlist_internalmodlist_linked分别保存不同类型的模块。在这篇文章中,我们谈论的是第一个BUILTIN类型。我发出这些链表的定义位置:/*src/node.cc:175*/staticnode_module*modlist_builtin;//我们只关注内置模块staticnode_module*modlist_internal;staticnode_module*modlist_linked;staticnode_module*modlist_addon;总结这个原生模块的注册过程写到这里,逻辑比较简单,但是连续的宏定义使得代码不那么直观。原生模块加载写好后,接下来,我们继续写原生模块的加载章节。作者Maslow(wangfugen@126.com),laf.js的作者。lafyun.com是一个开源的云开发平台,前端变成全栈,不需要服务器。