1. 先从 Nodejs 的入口谈起,一下采纳的是 nodejs 6.9.4 版本。
nodejs 启动的入口文件 src/node_main.cc
// UNIX
int main(int argc, char *argv[]) {// Disable stdio buffering, it interacts poorly with printf()
// calls elsewhere in the program (e.g., any logging from V8.)
setvbuf(stdout, nullptr, _IONBF, 0);
setvbuf(stderr, nullptr, _IONBF, 0);
return node::Start(argc, argv);
}
由代码可看出,次要运行的是 node::Start 办法,此办法定义在 src/node.cc 文件中。
int Start(int argc, char** argv) {PlatformInit();
CHECK_GT(argc, 0);
// Hack around with the argv pointer. Used for process.title = "blah".
argv = uv_setup_args(argc, argv);
// This needs to run *before* V8::Initialize(). The const_cast is not
// optional, in case you're wondering.
int exec_argc;
const char** exec_argv;
Init(&argc, const_cast<const char**>(argv), &exec_argc, &exec_argv);
#if HAVE_OPENSSL
#ifdef NODE_FIPS_MODE
// In the case of FIPS builds we should make sure
// the random source is properly initialized first.
OPENSSL_init();
#endif // NODE_FIPS_MODE
// V8 on Windows doesn't have a good source of entropy. Seed it from
// OpenSSL's pool.
V8::SetEntropySource(crypto::EntropySource);
#endif
v8_platform.Initialize(v8_thread_pool_size);
V8::Initialize();
int exit_code = 1;
{
NodeInstanceData instance_data(NodeInstanceType::MAIN,
uv_default_loop(),
argc,
const_cast<const char**>(argv),
exec_argc,
exec_argv,
use_debug_agent);
StartNodeInstance(&instance_data);
exit_code = instance_data.exit_code();}
V8::Dispose();
v8_platform.Dispose();
delete[] exec_argv;
exec_argv = nullptr;
return exit_code;
}
此办法开端处调用了同一个文件中的 StartNodeInstance 办法,传入一个 NodeInstanceData(node_internals.h 中定义)实例,在这个办法中调用了 LoadEnvironment 办法,LoadEnvironment 办法有如下代码:
// Execute the lib/internal/bootstrap_node.js file which was included as a
// static C string in node_natives.h by node_js2c.
// 'internal_bootstrap_node_native' is the string containing that source code.
Local<String> script_name = FIXED_ONE_BYTE_STRING(env->isolate(),
"bootstrap_node.js");
Local<Value> f_value = ExecuteString(env, MainSource(env), script_name);
if (try_catch.HasCaught()) {ReportException(env, try_catch);
exit(10);
}
// The bootstrap_node.js file returns a function 'f'
CHECK(f_value->IsFunction());
Local<Function> f = Local<Function>::Cast(f_value);
// Add a reference to the global object
Local<Object> global = env->context()->Global();
#if defined HAVE_DTRACE || defined HAVE_ETW
InitDTrace(env, global);
#endif
#if defined HAVE_LTTNG
InitLTTNG(env, global);
#endif
#if defined HAVE_PERFCTR
InitPerfCounters(env, global);
#endif
// Enable handling of uncaught exceptions
// (FatalException(), break on uncaught exception in debugger)
//
// This is not strictly necessary since it's almost impossible
// to attach the debugger fast enought to break on exception
// thrown during process startup.
try_catch.SetVerbose(true);
env->SetMethod(env->process_object(), "_rawDebug", RawDebug);
// Expose the global object as a property on itself
// (Allows you to set stuff on `global` from anywhere in JavaScript.)
global->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "global"), global);
// Now we call 'f' with the 'process' variable that we've built up with
// all our bindings. Inside bootstrap_node.js and internal/process we'll
// take care of assigning things to their places.
// We start the process this way in order to be more modular. Developers
// who do not like how bootstrap_node.js sets up the module system but do
// like Node's I/O bindings may want to replace'f' with their own function.
Local<Value> arg = env->process_object();
f->Call(Null(env->isolate()), 1, &arg);
能够看到,这个 node 通过执行 /lib/internal/bootstrap_node.js 进行初始化,ExecuteString 之后失去的是 bootstrap_node 的一个对象,将其类型转换成 Function,在最初执行之 f ->Call(Null(env->isolate()), 1, &arg); 执行的时候传入的 &arg 就是 process 对象的援用。而 process 对象的设置是在雷同文件的 SetupProcessObject 函数中设置的,在这个调用过程中在 process 对象上设置了除 bind 和 dlopen 外的简直所有办法和对象。bootstrap_node.js 执行初始化的就是 Nodejs 的 module 模块零碎。
2.Node 启动后会首先加载其外围模块(既纯用 C ++ 编写的 Nodejs 自带模块)。而在 js 中援用这些模块都是应用 process.binding 函数进行援用,在 process 的初始化函数 SetupProcessObject 中能够看到如下代码:
env->SetMethod(process, "binding", Binding);
也就是说理论应用的是 node.cc 文件中的 Binding 函数,如下:
node_module* mod = get_builtin_module(*module_v);
if (mod != nullptr) {exports = Object::New(env->isolate());
// Internal bindings don't have a"module" object, only exports.
CHECK_EQ(mod->nm_register_func, nullptr);
CHECK_NE(mod->nm_context_register_func, nullptr);
Local<Value> unused = Undefined(env->isolate());
mod->nm_context_register_func(exports, unused,
env->context(), mod->nm_priv);
cache->Set(module, exports);
} else if (!strcmp(*module_v, "constants")) {exports = Object::New(env->isolate());
DefineConstants(env->isolate(), exports);
cache->Set(module, exports);
} else if (!strcmp(*module_v, "natives")) {exports = Object::New(env->isolate());
DefineJavaScript(env, exports);
cache->Set(module, exports);
} else {char errmsg[1024];
snprintf(errmsg,
sizeof(errmsg),
"No such module: %s",
*module_v);
return env->ThrowError(errmsg);
}
args.GetReturnValue().Set(exports);
能够看到,是调用 get_builtin_module 函数来获取编译好的 c ++ 模块的,看下 get_builtin_module 函数:
struct node_module* get_builtin_module(const char* name) {
struct node_module* mp;
for (mp = modlist_builtin; mp != nullptr; mp = mp->nm_link) {if (strcmp(mp->nm_modname, name) == 0)
break;
}
CHECK(mp == nullptr || (mp->nm_flags & NM_F_BUILTIN) != 0);
return (mp);
}
能够看到,这个函数在 modlist_builtin 构造中寻找对应输出名称的模块并返回。modlist_builtin 构造是在 node_module_register 函数(在 node.cc 文件中被定义)调用时被逐条注册的,而 node_module_register 调用的中央是在 node.h 中的宏定义
#define NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, priv, flags) \
extern "C" { \
static node::node_module _module = \
{ \
NODE_MODULE_VERSION, \
flags, \
NULL, \
__FILE__, \
NULL, \
(node::addon_context_register_func) (regfunc), \
NODE_STRINGIFY(modname), \
priv, \
NULL \
}; \
NODE_C_CTOR(_register_ ## modname) { \
node_module_register(&_module); \
} \
}
#define NODE_MODULE_CONTEXT_AWARE_BUILTIN(modname, regfunc) \
NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, NULL, NM_F_BUILTIN) \
在每个 src 目录下的 c ++ 模块都会在结尾处调用 NODE_MODULE_CONTEXT_AWARE_BUILTIN,也就是在模块被引入后就会将模块注册到 modlist_builtin 中去。供后续被其余模块援用。