一直想着看Nodej源码,断断续续的折腾了一下,但总串不起来,太久不看又忘记。决心每天看一点,特地记录在这里,作为逼迫自己的动力。

2019/09/22 一、源码编译

之前在电脑上了下源码,源码目录截图:

编译通过了,编译命令:make -j4

尝试修改下源码文件:lib/http.js,加入一行打印代码:

之后,编译 make -j4,第一次编译会花点时间,之后编译会快很多。编译之后,在当前目录下生成out/Release目录。

看看修改的代码是不是有效:

cd out/Release
./node
require('http')

可以看到打印出了required http haha,修改成功。

2019/09/23 二、node进程启动

入口文件:src/node_main.cc,

 
 _WIN32 用来判断是否是windows平台。
  之后还有 __linux__、__POSIX__ 这样的宏,但都不属于mac上的宏,所以这部分可以先忽略。值得注意的是NODE_SHARED_MODE定义在node.gypi里,不太懂。(2019/09/28 增加)
 
然后进入main函数 :
设置标准输出、标准错误输出不带缓冲,立即输出到控制台:
  // Disable stdio buffering, it interacts poorly with printf()
// calls elsewhere in the program (e.g., any logging from V8.)
setvbuf(stdout, nullptr, _IONBF, );
setvbuf(stderr, nullptr, _IONBF, );

再调用了node::Start函数,传入参数。

int main(int argc, char* argv[]) {
 ... return node::Start(argc, argv);
}

比如说使用 node /xxxx/test.js 执行时,argc是2,argv则是['/xxxx/node', '/xxxx/test.js'],第一个是node的执行路径。

再来看node::Start函数在src/node.cc文件:

int Start(int argc, char** argv) {
    atexit([] () { uv_tty_reset_mode(); }); // 注册退出时执行的函数

    PlatformInit(); // 平台初始化,在mac上好像可以略过
    performance::performance_node_start = PERFORMANCE_NOW(); // 简单理解为当前时间
    argv = uv_setup_args(argc, argv);//存储参数Store the program arguments. Required for uv_get_process_title/uv_set_process_title argv还是没变
  std::vector<std::string> args(argv, argv + argc);  // 创建argv的拷贝
  std::vector<std::string> exec_args;
// This needs to run *before* V8::Initialize().
Init(&args, &exec_args);
 ...
v8_platform.Initialize(
per_process_opts->v8_thread_pool_size);
V8::Initialize();
performance::performance_v8_start = PERFORMANCE_NOW();
v8_initialized = true;
const int exit_code =
Start(uv_default_loop(), args, exec_args);

v8_platform.StopTracingAgent();
v8_initialized = false;
V8::Dispose();
v8_platform.Dispose();
return exit_code;
}

可以大概看出流程:

一、Init函数初始化了些什么东西。

Init函数挺有意思,

void Init(std::vector<std::string>* argv,
std::vector<std::string>* exec_argv) {
// Initialize prog_start_time to get relative uptime.
prog_start_time = static_cast<double>(uv_now(uv_default_loop())); // Register built-in modules
RegisterBuiltinModules();
其中的RegisterBuiltinModules定义如下:
void RegisterBuiltinModules() {
#define V(modname) _register_##modname();
NODE_BUILTIN_MODULES(V)
#undef V
}

只是调用了NODE_BUILTIN_MODULES,传入了一个宏定义。而 NODE_BUILTING_MODULES定义在node_internals.h头文件:

#define NODE_BUILTIN_MODULES(V)                                               \
NODE_BUILTIN_STANDARD_MODULES(V) \
NODE_BUILTIN_OPENSSL_MODULES(V) \
NODE_BUILTIN_ICU_MODULES(V)
NODE_BUILTIN_STANDARD_MODULES: 
#define NODE_BUILTIN_STANDARD_MODULES(V)                                      \
V(async_wrap) \
V(buffer) \
V(cares_wrap) \
V(config) \
V(contextify) \
V(domain) \
V(fs) \
V(fs_event_wrap) \
V(heap_utils) \
V(http2) \
V(http_parser) \
V(inspector) \
V(js_stream) \
V(messaging) \
V(module_wrap) \
V(options) \
V(os) \
V(performance) \
V(pipe_wrap) \
V(process_wrap) \
V(serdes) \
V(signal_wrap) \
V(spawn_sync) \
V(stream_pipe) \
V(stream_wrap) \
V(string_decoder) \
V(symbols) \
V(tcp_wrap) \
V(timer_wrap) \
V(trace_events) \
V(tty_wrap) \
V(types) \
V(udp_wrap) \
V(url) \
V(util) \
V(uv) \
V(v8) \
V(worker) \
V(zlib)

宏之间的互相调用,但是在node_internals.h里找不到async_wrap的定义,不知道在哪里定义的。(2019/09/28 增加)

二、初始化v8引擎。

三、调用Start函数,传入事件循环。具体看Start函数。

Start函数还是在node.cc文件:

inline int Start(uv_loop_t* event_loop,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args) {
 ...
Isolate* const isolate = NewIsolate(allocator.get());
{
Mutex::ScopedLock scoped_lock(node_isolate_mutex);
CHECK_NULL(node_isolate);
node_isolate = isolate;
} int exit_code;
{
  ...
// 又调用了重载函数
exit_code = Start(isolate, isolate_data.get
(), args, exec_args);
}
 ...
isolate->Dispose();
return exit_code;
}

找到重载函数Start:

inline int Start(Isolate* isolate, IsolateData* isolate_data,
const std::vector<std::string>& args,
const std::vector<std::string>& exec_args) {
HandleScope handle_scope(isolate);
Local<Context> context = NewContext(isolate);
Context::Scope context_scope(context);
Environment env(isolate_data, context, v8_platform.GetTracingAgentWriter());
env.Start(args, exec_args, v8_is_profiling); const char* path = args.size() > ? args[].c_str() : nullptr;
...
{
Environment::AsyncCallbackScope callback_scope(&env);
env.async_hooks()->push_async_ids(, );
LoadEnvironment(&env);
env.async_hooks()->pop_async_id();
}
}

继续LoadEnvironment函数。

LoadEnvironment函数:

void LoadEnvironment(Environment* env) {
HandleScope handle_scope(env->isolate());
...
// 读internal/bootstrap/loaders.js文件内容
Local<String> loaders_name =
FIXED_ONE_BYTE_STRING(env->isolate(), "internal/bootstrap/loaders.js"); // 包裹刚才读取的loaders.js内容
MaybeLocal<Function> loaders_bootstrapper =
GetBootstrapper(env, LoadersBootstrapperSource(env), loaders_name);  // 读取node.js文件内容
Local<String> node_name =
FIXED_ONE_BYTE_STRING(env->isolate(), "internal/bootstrap/node.js"); // 包裹node.js,
MaybeLocal<Function> node_bootstrapper =
GetBootstrapper(env, NodeBootstrapperSource(env), node_name); Local<Value> loaders_bootstrapper_args[] = {
env->process_object(),
get_binding_fn,
get_linked_binding_fn,
get_internal_binding_fn,
Boolean::New(env->isolate(),
env->options()->debug_options->break_node_first_line)
}; // Bootstrap internal loaders
Local<Value> bootstrapped_loaders;
// 执行loaders.js,得到一个对象:{ internalBinding, NativeModule }
if (!ExecuteBootstrapper(env, loaders_bootstrapper.ToLocalChecked(),
arraysize(loaders_bootstrapper_args),
loaders_bootstrapper_args,
&bootstrapped_loaders)) {
return;
} // Bootstrap Node.js
Local<Object> bootstrapper = Object::New(env->isolate());
SetupBootstrapObject(env, bootstrapper);
Local<Value> bootstrapped_node;
Local<Value> node_bootstrapper_args[] = {
env->process_object(),
bootstrapper,
bootstrapped_loaders
};
// node.js是一系列的函数调用,设置node进程全局变量,如设置process对象属性值
if (!ExecuteBootstrapper(env, node_bootstrapper.ToLocalChecked(),
arraysize(node_bootstrapper_args),
node_bootstrapper_args,
&bootstrapped_node)) {
return;
}
}

之后,就是进入事件循环。

最新文章

  1. try...catch..finally
  2. 常让人误解的一道js小题
  3. spring mvc CommonsMultipartResolver上传文件异常处理
  4. Android常用功能代码块
  5. PHP获取APK的包信息
  6. 【android学习4】Eclipse中Clean作用
  7. 在TextView上加上下划线或中划线
  8. Python学习笔记-Day3-python函数
  9. go get 获得 golang.org 的项目
  10. Model元数据解析
  11. Eclipse 4.2 + Tomcat 7.x + JDK 7 搭建Java Web开发环境
  12. JQuery插件开发初探——图片轮播
  13. 升讯威微信营销系统开发实践:(5) Github 源码:微信接口的 .NET 封装。
  14. Android6.0-运行时权限处理
  15. [ACdream]瑶瑶带你玩激光坦克
  16. 视音频编解码学习工程:AAC格式分析器
  17. 定时获取MySQL库的大小
  18. Android 接收系统广播(动态和静态)
  19. linux-shell-变量参数
  20. 微信小程序 scroll-view 实现锚点跳转

热门文章

  1. Python学习记录6-list、tuple、dict、set复习
  2. Django获取用户form表单
  3. uuid:全局唯一标识符
  4. 一 :了解MVC
  5. 利用 sendBeacon 发送统计信息
  6. mysql——数据库存储引擎
  7. 实用:Java基础流计算
  8. java线程基础巩固---创建并启动线程
  9. R中数据的输入和数据的标注
  10. duilib学习领悟(1)