node.js简述
node.js是嵌入式v8应用。node.js为v8提供运行时,v8为node提供javaScript的编译执行。另外node.js实现了诸如文件系统、模块、包、操作系统 API、网络通信等功能模块。为此我们能在node中可以读取文件,访问socket创建网络端口等。
与node不同的是,chromium也是v8的嵌入式应用。chromium为v8提供了运行时。但chromium并没有为v8提供了文件系统等功能模块。这由于网页是一种动态加载的文件,对于操作系统来说,他是一个不可信的文件类型,因此chromium并没有为v8提供直接访问本地文件的能力。但chromium为v8提供了操作网页功能,ajax网络访问。因此可以在chromium中v8中通过javaScript去创建图形界面,通过canvas去画图。通过webgl去执行opengl命令等。
node.js依靠v8提供了javaScript解析执行。依靠libuv提供了事件循环,跨平台文件网络io等能力。整个node.js应用都围绕着着两个模块做展开。再提供一些commonjs运行时的内建javaScript模块等和运行时内置对象共同组成了node。依靠本人的经验。node.js的实现并不优雅,模块拆分也很乱甚至有点糟糕的地步。整体实现难度中等。后面有打算写一个教程,关于如何从零开始编写一个node。
node源码启动流程分析
流程有点长,请仔细阅读。下面的内容会围绕着这个展开。本下面都是在windows系统下讨论。node.js的启动流程(wps真香)
node_main.cc
的wmain()
为windows系统下的node入口函数。在入口函数中只要是做了windows操作系统的判断。并把参数由utf-16编码转换成utf-8的格式。最终调用node::Start()
函数执行下面的流程。 ```cpp
int wmain(int argc, wchar_t* wargv[]) {
char buf[SKIP_CHECK_SIZE + 1];
// 操作系统的判断
if (!IsWindows8Point1OrGreater() &&
!(IsWindowsServer() && IsWindows8OrGreater()) &&
(GetEnvironmentVariableA(SKIP_CHECK_VAR, buf, sizeof(buf)) !=
SKIP_CHECK_SIZE ||
strncmp(buf, SKIP_CHECK_VALUE, SKIP_CHECK_SIZE + 1) != 0)) {
fprintf(stderr, “Node.js is only supported on Windows 8.1, Windows “
“Server 2012 R2, or higher.\n”
“Setting the “ SKIP_CHECK_VAR “ environment variable “
“to 1 skips this\ncheck, but Node.js might not execute “
“correctly. Any issues encountered on\nunsupported “
“platforms will not be fixed.”);
exit(ERROR_EXE_MACHINE_TYPE_MISMATCH);
}
// c语言的字符串后面需要加\0结尾
char* argv = new char[argc + 1];
// 把参数的编码有utf-16装换成utf-8 for (int i = 0; i < argc; i++) {
DWORD size = WideCharToMultiByte(CP_UTF8,
0,
wargv[i],
-1,
nullptr,
0,
nullptr,
nullptr);
if (size == 0) {
fprintf(stderr, "Could not convert arguments to utf8.");
exit(1);
}
argv[i] = new char[size];
DWORD result = WideCharToMultiByte(CP_UTF8,
0,
wargv[i],
-1,
argv[i],
size,
nullptr,
nullptr);
if (result == 0) {
fprintf(stderr, "Could not convert arguments to utf8.");
exit(1);
}
} argv[argc] = nullptr; // 执行主流程 return node::Start(argc, argv); }
2. `node.cc ` 整个node的流程都在文件的`Start()` 函数中做处理。下面流程也是在这个函数的执行展开。
```cpp
int Start(int argc, char** argv) {
/* InitializeOncePerProcess 函数代表着只下只执行一次初始化,该函数只要做了一下的初始化工作
* 1:平台的初始化,只要是出入输出
* 2:初始化node的逻辑,包裹内建原生模块注册,内建javaScript模块的缓存数据的初始化,环境变量,icu更国际化等
* 3: v8的初始化
*/
InitializationResult result = InitializeOncePerProcess(argc, argv);
if (result.early_return) {
return result.exit_code;
}
{
// 创建v8隔离实例所需要的参数
Isolate::CreateParams params;
const std::vector<size_t>* indexes = nullptr;
const EnvSerializeInfo* env_info = nullptr;
bool force_no_snapshot = per_process::cli_options->per_isolate->no_node_snapshot;
// 是否使用快照的方式启动, 该快照由node自定义的快照文件。
if (!force_no_snapshot) {
// 获取快照数据
v8::StartupData* blob = NodeMainInstance::GetEmbeddedSnapshotBlob();
if (blob != nullptr) {
// 设置快照数据
params.snapshot_blob = blob;
indexes = NodeMainInstance::GetIsolateDataIndexes();
env_info = NodeMainInstance::GetEnvSerializeInfo();
}
}
// libuv事件循环配置
uv_loop_configure(uv_default_loop(), UV_METRICS_IDLE_TIME);
// 创建node的主实例。整个node的运行都是围绕着NodeMainInstance实例开展
NodeMainInstance main_instance(¶ms,
uv_default_loop(),
per_process::v8_platform.Platform(),
result.args,
result.exec_args,
indexes);
// 执行主流程
result.exit_code = main_instance.Run(env_info);
}
TearDownOncePerProcess();
return result.exit_code;
}
InitializeOncePerProcess()
初始化函数,在该函数执行的初始化在node进程内只会执行一次。InitializeNodeWithArgs()
初始化node。包括执行注册内建原生模块函数。设置命令行参数例如设置v8的一些flags(v8::V8::SetFlagsFromString(“—abort-on-uncaught-exception”)),处理环境变量(例如我们在环境变量中配置node的的libuv启用的线程数量默认是4,通过配置NODE_OPTIONS 的v8_thread_pool_size=4修改libuv默认启动线程数量。参数列表)。初始化内建javaScript模块的缓存cacheData。// 代码有省略,只要是v8的icu(国际化)初始化 ** * argv 命令行参数 * errors 生成的错误列表 */ int InitializeNodeWithArgs(std::vector<std::string>* argv, std::vector<std::string>* exec_argv, std::vector<std::string>* errors) { // 注册c++内建模块 binding::RegisterBuiltinModules(); // 初始化内建javaScript模块的CacheData NativeModuleEnv::InitializeCodeCache(); // 标记node 初始化完成 // node_is_initialized 为一个全局变量 node_is_initialized = true; return 0; }
v8的初始化,v8平台的初始化,包括线程池轨迹控制器等的初始化等。
// 内容做了删减 InitializationResult InitializeOncePerProcess(int argc, char** argv) { PlatformInit(); InitializationResult result; result.args = std::vector<std::string>(argv, argv + argc); std::vector<std::string> errors; // node的初始化, 上面做了讲解 { result.exit_code = InitializeNodeWithArgs(&(result.args), &(result.exec_args), &errors); 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; } } // 初始化v8平台 // 初始化v8的线程池的大小,v8的轨迹控制器 per_process::v8_platform.Initialize( static_cast<int>(per_process::cli_options->v8_thread_pool_size)); V8::Initialize(); // 标记v8已经进行了初始化 per_process::v8_initialized = true; return result; }
创建
NodeMainInstance
, 在NodeMainInstance
的实例创建v8隔离实例(Isolate), 并为v8隔离实例对象设置相关回调。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(); Isolate::Initialize(isolate_, *params); IsolateSettings s; // 设置隔离实例的回调函数 SetIsolateMiscHandlers(isolate_, s); }
NodeMainInstance::Run()
执行整个node应用的核心。创建Environment。Environment是node中一个重要的类,类中包含了属性包括隔离实例,执行上下文等属性,还有node中用于加载内建模块和原生模块的函数。在创建该对象后,在往后的操作调用函数都使用了Environment对象作为参数传递给每个调用的函数。更准确来说。Environment对象是node中的上下文对象。int NodeMainInstance::Run(const EnvSerializeInfo* env_info) { Locker locker(isolate_); Isolate::Scope isolate_scope(isolate_); HandleScope handle_scope(isolate_); int exit_code = 0; // 创建Environment 对象,包括创建v8执行上下文。并为执行上下的全局代理对象添加全局属性 DeleteFnPtr<Environment, FreeEnvironment> env = CreateMainEnvironment(&exit_code, env_info); { Context::Scope context_scope(env->context()); if (exit_code == 0) { // 执初始化libuv,执行主模块 LoadEnvironment(env.get(), StartExecutionCallback{}); // 执行事件循环 exit_code = SpinEventLoop(env.get()).FromMaybe(1); } return exit_code; }
通过
NodeMainInstance::CreateMainEnvironment()
创建Environment对象。 ```cpp DeleteFnPtrNodeMainInstance::CreateMainEnvironment(int exit_code,const EnvSerializeInfo envinfo) { *exit_code = 0; HandleScope handle_scope(isolate); // 期待隔离实例堆分析 if (isolatedata->options()->trackheap_objects) { isolate->GetHeapProfiler()->StartTrackingHeapObjects(true); }
Local
8. 创建v8执行上下文。
```cpp
Local<Context> NewContext(Isolate* isolate,
Local<ObjectTemplate> object_template) {
auto context = Context::New(isolate, nullptr, object_template);
if (context.IsEmpty()) return context;
// 初始化执行上下文,
if (!InitializeContext(context)) {
return Local<Context>();
}
return context;
}
执行上下文的初始化。
bool InitializeContext(Local<Context> context) {
// 通过快照去初始化v8执行上下文
if (!InitializeContextForSnapshot(context)) {
return false;
}
// 初始化执行上下运行时,只要是移除全局对象上一个导致bug的属性。
InitializeContextRuntime(context);
return true;
}
void InitializeContextRuntime(Local<Context> context) {
Isolate* isolate = context->GetIsolate();
HandleScope handle_scope(isolate);
// 删除 `Intl.v8BreakIterator`
Local<String> intl_string = FIXED_ONE_BYTE_STRING(isolate, "Intl");
Local<String> break_iter_string = FIXED_ONE_BYTE_STRING(isolate, "v8BreakIterator");
Local<Value> intl_v;
if (context->Global()->Get(context, intl_string).ToLocal(&intl_v) && intl_v->IsObject()) {
Local<Object> intl = intl_v.As<Object>();
intl->Delete(context, break_iter_string).Check();
}
// 删除 `Atomics.wake`
Local<String> atomics_string = FIXED_ONE_BYTE_STRING(isolate, "Atomics");
Local<String> wake_string = FIXED_ONE_BYTE_STRING(isolate, "wake");
Local<Value> atomics_v;
if (context->Global()->Get(context, atomics_string).ToLocal(&atomics_v) &&
atomics_v->IsObject()) {
Local<Object> atomics = atomics_v.As<Object>();
atomics->Delete(context, wake_string).Check();
}
// __proto__
Local<String> object_string = FIXED_ONE_BYTE_STRING(isolate, "Object");
Local<String> prototype_string = FIXED_ONE_BYTE_STRING(isolate, "prototype");
Local<Object> prototype = context->Global()
->Get(context, object_string)
.ToLocalChecked()
.As<Object>()
->Get(context, prototype_string)
.ToLocalChecked()
.As<Object>();
Local<String> proto_string = FIXED_ONE_BYTE_STRING(isolate, "__proto__");
if (per_process::cli_options->disable_proto == "delete") {
prototype->Delete(context, proto_string).ToChecked();
} else if (per_process::cli_options->disable_proto == "throw") {
Local<Value> thrower =
Function::New(context, ProtoThrower).ToLocalChecked();
PropertyDescriptor descriptor(thrower, thrower);
descriptor.set_enumerable(false);
descriptor.set_configurable(true);
prototype->DefineProperty(context, proto_string, descriptor).ToChecked();
} else if (per_process::cli_options->disable_proto != "") {
// Validated in ProcessGlobalArgs
FatalError("InitializeContextRuntime()", "invalid --disable-proto mode");
}
}
- 初始化v8执行上下文。在
Environment::InitializeMainContext()
函数上执行v8执行上下文的初始化。创建进程对象(process),创建primordials对象,primordials对象保存着标准javaScript全局对象原型上的函数的应用集合。 ```cpp void Environment::InitializeMainContext(Localcontext,
context.Reset(context->GetIsolate(), context); // 创建属性 CreateProperties(); } void Environment::CreateProperties() { HandleScope handle_scope(isolate); Localconst EnvSerializeInfo* env_info) {
ctx = context();
// 获取每个上下文需要的一些binding对象。 Local