1 c++模块的注册和使用
我们知道nodejs是由js、c++、c组成的。今天我们来看一下他们是如何分工和合作的。本文以net模块为例进行分析。我们可以通过以下方式使用net模块。
const net = require('net');
net模块是原生的js模块。对应nodejs源码的net.js。他是对tcp和pipe的封装,我们这里只讲tcp的功能。我们可以通过以下代码创建一个tcp服务器。
const net = require('net');
net.createServer((socket) => {}).listen(80);
我们知道js里是没有网络功能的,这就意味着,网络功能是由nodejs中的c++模块实现的。所以这时候nodejs就会创建一个c++对象。
const {
TCP,
} = internalBinding('tcp_wrap');
new TCP(TCPConstants.SERVER);
TCP对象封装了底层tcp的功能。他对应的是c++层的tcp_wrap模块。我们看看tcp_wrap模块的代码。在分析tcp_wrap之前我们先看看internalBinding做了什么事情。
let internalBinding;
{
const bindingObj = ObjectCreate(null);
internalBinding = function internalBinding(module) {
let mod = bindingObj[module];
if (typeof mod !== 'object') {
mod = bindingObj[module] = getInternalBinding(module);
}
return mod;
};
}
internalBinding是在getInternalBinding的基础上加了缓存处理。我们继续看getInternalBinding。
// 根据模块名查找对应的模块
void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
// 模块名
Local<String> module = args[0].As<String>();
node::Utf8Value module_v(env->isolate(), module);
Local<Object> exports;
// 查找名字为module_v并且标记位NM_F_INTERNAL的模块
node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
exports = InitModule(env, mod, module);
args.GetReturnValue().Set(exports);
}
getInternalBinding通过模块名从模块链表里找到对应的节点。然后执行对应的初始化函数。
// 初始化一个模块,即执行他里面的注册函数
static Local<Object> InitModule(Environment* env,
node_module* mod,
Local<String> module) {
Local<Object> exports = Object::New(env->isolate());
Local<Value> unused = Undefined(env->isolate());
mod->nm_context_register_func(exports, unused, env->context(), mod->nm_priv);
return exports;
}
那么链表中的模块是从哪里来的呢?这时候我们就可以回头分析tcp_wrap了。tcp_wrap.cc的最后一句是(Initialize函数即上面的nm_context_register_func属性的值)