NodeJs 启动代码解析(一)
文章目录
NodeJs 的 main 函数在 node_main.cc 文件中, NodeJs 区分了 WIN32
、 UNIX
、 LINUX
。我们主要分析 Linux 部分。
简化一下代码,我们得到 main 函数的主要代码。
int main(int argc, char* argv[]) {
node::per_process::linux_at_secure = getauxval(AT_SECURE);
// 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
是 NodeJs 部分的 namespace ,与 V8 的相区分。
node::Start(argc, argv)
则是整个 Node 开始的部分。
node::Start(argc, argv)
代码在 node.cc 源文件里。
int Start(int argc, char** argv) {
InitializationResult result = InitializeOncePerProcess(argc, argv);
if (result.early_return) {
return result.exit_code;
}
{
Isolate::CreateParams params;
const std::vector<size_t>* indices = nullptr;
const EnvSerializeInfo* env_info = nullptr;
bool use_node_snapshot =
per_process::cli_options->per_isolate->node_snapshot;
if (use_node_snapshot) {
v8::StartupData* blob = NodeMainInstance::GetEmbeddedSnapshotBlob();
if (blob != nullptr) {
params.snapshot_blob = blob;
indices = NodeMainInstance::GetIsolateDataIndices();
env_info = NodeMainInstance::GetEnvSerializeInfo();
}
}
uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME);
NodeMainInstance main_instance(¶ms,
uv_default_loop(),
per_process::v8_platform.Platform(),
result.args,
result.exec_args,
indices);
result.exit_code = main_instance.Run(env_info);
}
TearDownOncePerProcess();
return result.exit_code;
}
首先,与以前的 Node 相比,新版的 Node 多了 snapshot 功能,因此代码在这里也多了一层 snapshot 检测。
InitializationResult result = InitializeOncePerProcess(argc, argv);
if (result.early_return) {
return result.exit_code;
}
InitializeOncePerProcess
函数调用了它自身的另一个多态方法。该方法初始化了一些 C++ 模块,比如说 crypto
,然后初始化了 V8 引擎。
uv_loop_configure
函数对 libuv 进行了配置。
之后创建了 NodeMainInstance
实例。
TearDownOncePerProcess
则是程序运行完成后的收尾处理。
InitializeOncePerProcess(argc, argv)
InitializeOncePerProcess(argc, arg)
默认启用了一些参数。
InitializationResult InitializeOncePerProcess(int argc, char** argv) {
return InitializeOncePerProcess(argc, argv, kDefaultInitialization);
}
其中 kDefaultInitialization
是一个枚举值。
enum InitializationSettingsFlags : uint64_t {
kDefaultInitialization = 1 << 0,
kInitializeV8 = 1 << 1,
kRunPlatformInit = 1 << 2,
kInitOpenSSL = 1 << 3
};
InitializeOncePerProcess
源码非常长,在此就不列出来了,它也在 node.cc 源文件中。
在初始化 V8 前,首先需要对传入参数进行解析。
InitializationResult result;
result.args = std::vector<std::string>(argv, argv + argc);
std::vector<std::string> errors;
// This needs to run *before* V8::Initialize().
{
result.exit_code = InitializeNodeWithArgs(
&(result.args), &(result.exec_args), &errors, process_flags);
for (const std::string& error : errors)
fprintf(stderr, "%s: %s\n", result.args.at(0).c_str(), error.c_str());
if (result.exit_code != 0) {
result.early_return = true;
return result;
}
}
result
就是最后要传出去的值。
InitializeNodeWithArgs
主要对内建模块进行了初始化,同时设置了环境变量,并解析传入的参数格式,如果格式错误就会报错。
后续主要是判断是否可以启用 crypto 模块,完成后便设置进 V8 里面。
最后就启动 V8 。
per_process::v8_platform.Initialize(
static_cast<int>(per_process::cli_options->v8_thread_pool_size));
if (init_flags & kInitializeV8) {
V8::Initialize();
}
performance::performance_v8_start = PERFORMANCE_NOW();
per_process::v8_initialized = true;
Node 实例
Node 实例的源码在源文件 node_main_instance.cc 源文件中。
初始化
NodeMainInstance::NodeMainInstance(
Isolate::CreateParams* params,
uv_loop_t* event_loop,
MultiIsolatePlatform* platform,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args,
const std::vector<size_t>* per_isolate_data_indexes)
: args_(args),
exec_args_(exec_args),
array_buffer_allocator_(ArrayBufferAllocator::Create()),
isolate_(nullptr),
platform_(platform),
isolate_data_(nullptr),
owns_isolate_(true) {
// ...
isolate_ = Isolate::Allocate();
CHECK_NOT_NULL(isolate_);
// Register the isolate on the platform before the isolate gets initialized,
// so that the isolate can access the platform during initialization.
platform->RegisterIsolate(isolate_, event_loop);
SetIsolateCreateParamsForNode(params);
Isolate::Initialize(isolate_, *params);
isolate_data_ = std::make_unique<IsolateData>(isolate_,
event_loop,
platform,
array_buffer_allocator_.get(),
per_isolate_data_indexes);
// ...
isolate_data_->max_young_gen_size =
params->constraints.max_young_generation_size_in_bytes();
}
我缩减了一些代码。由于我的代码稍微有点老,所以与当前的源码有所区别。
Node 在此创建了一个 Isolate
, Isolate
是 V8 里面的概念,对应 JS 引擎实例。
出了创建实例之外, Node 还设置了新生代最大空间。
Run(env_info)
Run
方法有两个重载,这是其中一个。
int NodeMainInstance::Run(const EnvSerializeInfo* env_info) {
Locker locker(isolate_);
Isolate::Scope isolate_scope(isolate_);
HandleScope handle_scope(isolate_);
int exit_code = 0;
DeleteFnPtr<Environment, FreeEnvironment> env =
CreateMainEnvironment(&exit_code, env_info);
CHECK_NOT_NULL(env);
Context::Scope context_scope(env->context());
Run(&exit_code, env.get());
return exit_code;
}
Node 首先对 isolate_
上了锁,并创建了一个作用域。 env
就是根据系统环境生成的环境信息。
Run(&exit_code, env.get())
void NodeMainInstance::Run(int* exit_code, Environment* env) {
if (*exit_code == 0) {
LoadEnvironment(env, StartExecutionCallback{});
*exit_code = SpinEventLoop(env).FromMaybe(1);
}
ResetStdio();
// ...
}
如果能够正常运行的话,就会启动 libuv 做事件循环。LoadEnvironment(env, StartExecutionCallback{});
进行初始化,并加载内部库。
MaybeLocal<Value> LoadEnvironment(
Environment* env,
StartExecutionCallback cb) {
env->InitializeLibuv();
env->InitializeDiagnostics();
return StartExecution(env, cb);
}
事件循环的主体在 SpinEventLoop(env)
里,代码主要在一个 do-while
里运行。
Maybe<int> SpinEventLoop(Environment* env) {
CHECK_NOT_NULL(env);
MultiIsolatePlatform* platform = GetMultiIsolatePlatform(env);
CHECK_NOT_NULL(platform);
Isolate* isolate = env->isolate();
HandleScope handle_scope(isolate);
Context::Scope context_scope(env->context());
SealHandleScope seal(isolate);
if (env->is_stopping()) return Nothing<int>();
env->set_trace_sync_io(env->options()->trace_sync_io);
{
bool more;
env->performance_state()->Mark(
node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
// 循环主体
do {
if (env->is_stopping()) break;
uv_run(env->event_loop(), UV_RUN_DEFAULT);
if (env->is_stopping()) break;
platform->DrainTasks(isolate);
more = uv_loop_alive(env->event_loop());
if (more && !env->is_stopping()) continue;
if (EmitProcessBeforeExit(env).IsNothing())
break;
// Emit `beforeExit` if the loop became alive either after emitting
// event, or after running some callbacks.
more = uv_loop_alive(env->event_loop());
} while (more == true && !env->is_stopping());
env->performance_state()->Mark(
node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
}
if (env->is_stopping()) return Nothing<int>();
env->set_trace_sync_io(false);
env->PrintInfoForSnapshotIfDebug();
env->VerifyNoStrongBaseObjects();
return EmitProcessExit(env);
}
这里的代码位于 embed_helpers.cc 源文件内。
从这里可以看出 Node 确实是单线程的。
文章作者 bigshans
上次更新 2022-05-06