node 模块载入原理【1】

软件发布|下载排行|最新软件

当前位置:首页IT学院IT技术

node 模块载入原理【1】

ubuntugx   2020-03-29 我要评论
## 简单介绍 我们会从简单的模块载入原理来开始,尝试阅读下 Node.js 源代码。首先我们知道 Node.js 的源代码主要是由 C++ 和 JavaScript 编写的,JS 部分主要在 lib 目录下,而 C++ 部分主要在 src 目录下。 ![](https://img2020.cnblogs.com/blog/1004427/202003/1004427-20200329151921980-987119585.png) 模块加载主要是分四种类型的模块: 1. C++ 核心模块:主要在 src 目录下,比如 node_file.cc 2. Node.js 内部模块:和 C++ 核心模块不同,在源码的 lib 目录下,以同名的 JS 源码来实现,实际上 Node.js 内置模块是对 C++ 核心模块的更高层次的封装,比如 fs、http 3. 用户源码模块 4. C++ 扩展模块:插件是用 C++ 编写的动态链接共享对象。 require() 函数可以将插件加载为普通的 Node.js 模块。 插件提供了 JavaScript 和 C/C++ 库之间的接口。 这次介绍的是 C++ 核心模块加载的源码分析 ## C++ 核心模块 一句话概括:每个 C++ 核心模块源码末尾都有一个宏调用,将模块注册到 C++ 核心模块的链表当中去,以供 `internalBinding` 获取它。 从这句话可以得到两个步骤:注册和获取。 ### 总的流程 ![](https://img2020.cnblogs.com/blog/1004427/202003/1004427-20200329152207598-817500759.png) 1. 入口文件执行 我们知道,Node.js 的执行入口在 node.cc 的 Start 函数 ```c++ int Start(int argc, char** argv) {} ``` 2. 分支一:C++ 模块注册到链表中 我们来看看 Start 函数都做了什么,首先调用了 InitializeOncePerProcess,对 Node.js 和 V8 做初始化处理。从这个函数找下去,可以找到 C++ 模块注册到链表 ```c++ InitializationResult result = InitializeOncePerProcess(argc, argv); ``` 3. 分支二:获取 C++ 模块 在 Start 函数继续找下去,可以看到实例化了 NodeMainInstance,调用了 Run 函数。从这个函数找下去,可以找到获取 C++ 模块的过程 ```c++ NodeMainInstance main_instance(...); result.exit_code = main_instance.Run(); ``` ### 分支一:C++ 模块注册到链表中 ![](https://img2020.cnblogs.com/blog/1004427/202003/1004427-20200329152336141-625291399.png) #### 1、前置链路 从上面的 `InitializeOncePerProcess` 开始往下找,可以看到调用了初始化 node 的函数 `InitializeNodeWithArgs` 函数 ```c++ result.exit_code = InitializeNodeWithArgs(&(result.args), &(result.exec_args), &errors); ``` 继续找,看到它调用了 node_binding.cc 中的 `RegisterBuiltinModules`,作用是注册 C++ 模块 ```c++ binding::RegisterBuiltinModules(); ``` #### 2、C++ 模块注册函数的调用 那我们就来看一下,`RegisterBuiltinModules` 这个函数都做了什么,主要做了两件事:一是定义了 `V` 这个宏,它接收 `modname` 这个参数;二是调用 `NODE_BUILTIN_MODULES` ```c++ void RegisterBuiltinModules() { #define V(modname) _register_##modname(); NODE_BUILTIN_MODULES(V) // module name 可以在这个宏找到,这里不多写了 #undef V } ``` 然后我们可以看到 `NODE_BUILTIN_MODULES` 其实也是一个宏定义: ```c++ #define NODE_BUILTIN_MODULES(V) \ NODE_BUILTIN_STANDARD_MODULES(V) \ ... ``` 其中 `NODE_BUILTIN_STANDARD_MODULES` 的定义是这样的: ```c++ #define NODE_BUILTIN_STANDARD_MODULES(V) \ V(async_wrap) \ V(buffer) \ ... ``` 也就是说,c++的预处理后,会变成下面的函数体 ```c++ void RegisterBuiltinModules() { _register_async_wrap(); _register_buffer(); .... } ``` 也就是说,最终去调用的是 `_register_async_wrap` 和 `_register_buffer` ...这些函数,好了,那么这些函数是在哪定义的呢? #### 3、C++ 模块注册函数的定义 从上面宏定义后面,可以找到这样的注释: The definitions are in each module's implementation when calling the NODE_MODULE_CONTEXT_AWARE_INTERNAL. 也就是说,定义是在每个模块调用 `NODE_MODULE_CONTEXT_AWARE_INTERNAL` 时进行的~ 好的于是我们看看 fs 模块对应的 C++ 文件 node_file.cc ,看到最后一行调用了 `NODE_MODULE_CONTEXT_AWARE_INTERNAL` ```c++ NODE_MODULE_CONTEXT_AWARE_INTERNAL(fs, node::fs::Initialize) ``` 继续,在 node_binding.h 看到它调用了 `NODE_MODULE_CONTEXT_AWARE_CPP` ```c++ #define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc) \ NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL) ``` 继续,在 node_binding.h 中,定义了` _register_##modname` 执行时调用了 `node_module_register` ```c++ #define NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \ void _register_##modname() { node_module_register(&_module); } ``` 好的最后一步,在 node_binding.cc 中找到了它的定义,将传入的 node module 插到了链表 `modlist_internal` 中,后面查找时就会在这里找了~ ```c++ extern "C" void node_module_register(void* m) { struct node_module* mp = reinterpret_cast

Copyright 2022 版权所有 软件发布 访问手机版

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 联系我们