1.先从Nodejs的入口谈起,一下采纳的是nodejs 6.9.4 版本。
nodejs启动的入口文件src/node_main.cc
// UNIXint 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中去。供后续被其余模块援用。