Node三种类型的模块加载的源码分析
发布于 3 年前 作者 chengang4505 4101 次浏览 来自 分享

node 有 3中类型的模块:

1、我们平常使用的普通模块,

2、node native 的js 模块,

3、node native 的c++ 模块,

这一个章主要就是通过源码分析这3中模块加载的细节。

1.普通模块的加载

文件:lib\module.js

普通模块加载用是lib\module.js中 Module.prototype.require 进行加载的

==> Module._load

==> module.load 加载的步骤是先检查cache,命中返回,再检查是否是native js 模块,命中返回,最后才执行加载

load的时候会根据文件名后缀调用相应的加载函数,

  var extension = path.extname(filename) || '.js';
  if (!Module._extensions[extension]) extension = '.js';
  Module._extensions[extension](this, filename);

我们这里以js为例

==> Module.prototype._compile 编译执行我们js内容

调用包裹的匿名函数,执行js 这个 args 对应 匿名函数的参数列表

(function (exports, require, module, __filename, __dirname) {})

大家注意这里的 require 是由一个函数调用返回的

var require = internalModule.makeRequireFunction.call(this);

internalModule 在 lib\internal\module.js 文件中定义。但是它最终调用的还是 Module.prototype.require ,只是包裹了一下 所以呢,平时使用的 require 就是这里传入的这个require了。

2. node native 的js 模块加载

文件: lib\internal\bootstrap_node.js

node native 的js 模块使用NativeModule.require进行加载的,NativeModule.require定义在 lib\internal\bootstrap_node.js (也就是node启动过程中的node.js,实际是bootstrap_node.js ) 文件中。

native js加载的时候也是先检查cache,没有命中才去加载native js。但是到哪里去加载nativa js呢,看下图中有一个变量 NativeModule._source ,它存储着 native js的所有模块信息,但是你又发现,它其实是通过 process.binding('natives') 加载的,而process.binding 是加载native c++ 模块的(待会下面会介绍),所以说呢,node把native js 所有模块当做一个名字是(natives)的native c++ 模块进行加载的

==> NativeModule.prototype.compile 接下来就是编译执行,但是在调用的时候参数都基本和普通模块的差不多,但是有一个区别,就是 require 对应的部分,在这个是传入的 NativeModule.require ,所以呢,我们平时用的 require 和 native js模块里面的 require 是不一样的,分别对应 Module.prototype.requireNativeModule.require

3.node native 的c++ 模块加载

文件: node.cc

node native 的c++ 模块是通过process.binding进行加载的,process 是 传入 bootstrap_node.js 的一个c++对象,上一章讲了 bootstrap_node.js 中会对process 进行部分信息初始化,,其实这只是很少的一部分,,大部分都在 c++ 部分初始化的。 process的初始化过程: 上一章讲node 启动过程中有一个 LoadEnvironment(env) 调用, 在这之前有个

Environment* env = CreateEnvironment(isolate, context, instance_data);

==> SetupProcessObject
进行process的初始化,部分信息如下: 这其中也包括加载c++模块的函数 binding 的初始化,

知道了 binding ,来分析 c++模块的加载,我们可以看出 process.binding 的调用最终会定位到 Binding 函数 加载前也会先检查cache,没有才调用 get_builtin_module 加载相应的模块。 到一个链表中 modlist_builtin 去查找相应的模块。

而所有的 c++模块都是通过一个宏添加到这个链表中的(在编译时),更多信息参考 bigtree9307的文章

#define NODE_MODULE_CONTEXT_AWARE_BUILTIN(modname, regfunc)    

补充:

native js 的所有模块字节信息加载 process.binding('natives') 加载, 上面说到node把native js 所有模块当做一个名字是(natives)的native c++ 模块进行加载的,如图: 当加载一个名字叫 natives 的c++模块的时候,就调用 DefineJavaScript 注意到DefineJavaScript 是遍历一个变量,其实 natives 是一个数组,定义在 node_natives.h(工具生成的头文件)中,里面存储着native js 字节数组配置信息, 里面的 internal_bootstrap_node_native,_debug_agent_native,assert_native…等等都是字节数字,存储着相应的native js 文件信息。

到这里基本上对3种模块的加载基本走了一遍,,有错的地方 ,希望大家指教。

1 回复

里面的 internal_bootstrap_node_native,_debug_agent_native,assert_native…等等都是字节数字,存储着相应的native js 文件信息。

这里面的内容是从哪里加载的

回到顶部