nodejs 源码浅析(一)——进程启动过程

项目文件结构

node项目结构比较简单,主要是两个文件夹,src和lib。其中src文件夹是node的C++源文件所在的目录,里面是进程的入口以及C++核心模块的代码。lib文件夹是js核心模块所在的目录,在编译的时候会使用v8自带的js2c.py(在tools目录下)将这些js代码编译成c代码,然后再编译进二进制可执行文件中。


node进程启动过程

带着问题去学习:

1.进程启动时有process对象,这个对象是怎么构建并传入javascript上下文的?
2.node的事件循环是怎么开始的?

通过一层层阅读代码,整理得进程启动调用时序图如下:

项目的入口文件是node_main.cc,main函数就定义在这里,从这里开始一步步去寻找。
在CreateEnvironment接口中首先声明了process对象,然后将它放进env对象中:

1
2
Local<Object> process_object = process_template->GetFunction()->NewInstance();
env->set_process_object(process_object);

在SetupProcessObject()中,通过READONLY_PROPERTY以及NODE_SET_METHOD宏,给process对象添加各种属性和方法:

1
2
NODE_SET_METHOD(process, "setgid", SetGid);
NODE_SET_METHOD(process, "getgid", GetGid);
1
2
3
4
5
6
READONLY_PROPERTY(versions,
"node",
OneByteString(env->isolate(), NODE_VERSION + 1));
READONLY_PROPERTY(versions,
"v8",
OneByteString(env->isolate(), V8::GetVersions()));

在LoadEnvironment()中主要是做两件事:
1.编译并执行node.js文件

1
2
Local<String> script_name = FIXED_ONE_BYTE_STRING(env->isolate(), "node.js");
Local<Value> f_value = ExecuteString(env, MainSource(env), script_name);

2.将process对象作为参数执行node.js文件.

1
f->Call(global, 1, &arg);

可以看到node.js文件主体是一个函数,编译执行node.js文件事实上只是将函数注册进了内存,第二步才是真正的执行。

在Start函数中:

1
2
3
4
5
6
7
8
9
10
11
do {
more = uv_run(env->event_loop(), UV_RUN_ONCE);
if (more == false) {
EmitBeforeExit(env);
// Emit `beforeExit` if the loop became alive either after emitting
// event, or after running some callbacks.
more = uv_loop_alive(env->event_loop());
if (uv_run(env->event_loop(), UN_RUN_NOWAIT) != 0)
more = true;
}
} while (more == true);