一、注册 C++ 模块

首先 Node.js 会调用 registerBuiltinModules 函数注册 C++模块,这个函数会调用一系列 registerxxx 的函数,我们发现在 Node.js 源码里找不到这些函数,因为这些函数会在各个 C++模块中,通过宏定义实现的。宏展开后就是上图黄色框的内容,每个 registerxxx 函数的作用就是往 C++模块的链表了插入一个节点,最后会形成一个链表。
那么 Node.js 里是如何访问这些 C++模块的呢?在 Node.js 中,是通过 internalBinding 访问 C++模块的,internalBinding 的逻辑很简单,就是根据模块名从模块队列中找到对应模块。但是这个函数只能在 Node.js 内部使用,不能在用户 js 模块使用。用户可以通过 process.binding 访问 C++模块。
二、创建 Environment 对象,并绑定到 Context
注册完 C++模块后就开始创建 Environment 对象,Environment 是 Node.js 执行时的环境对象,类似一个全局变量的作用,他记录了 Node.js 在运行时的一些公共数据。创建完 Environment 后,Node.js 会把该对象绑定到 V8 的 Context 中,为什么要这样做呢?主要是为了在 V8 的执行上下文里拿到 env 对象,因为 V8 中只有 Isolate、Context 这些对象。如果我们想在 V8 的执行环境中获取 Environment 对象的内容,就可以通过 Context 获取 Environment 对象。


三、初始化模块加载器
- Node.js 首先传入 c++模块加载器,执行 loader.js,loader.js 主要是封装了 c++模块加载器和原生 js 模块加载器。并保存到 env 对象中。
- 接着传入 c++和原生 js 模块加载器,执行 run_main_module.js。
- 在 run_main_module.js 中传入 js 和原生 js 模块加载器,执行用户的 js。
假设用户 js 如下
require('net')
require('./myModule')
分别加载了一个用户模块和原生 js 模块,我们看看加载过程,执行 require 的时候。
- Node.js 首先会判断是否是原生 js 模块,如果不是则直接加载用户模块,否则,会使用原生模块加载器加载原生 js 模块。
- 加载原生 js 模块的时候,如果用到了 c++模块,则使用 internalBinding 去加载。

四、执行用户 JS 代码,然后进入 Libuv 事件循环
接着 Node.js 就会执行用户的 js,通常用户的 js 会给事件循环生产任务,然后就进入了事件循环系统,比如我们 listen 一个服务器的时候,就会在事件循环中新建一个 tcp handle。Node.js 就会在这个事件循环中一直运行。
net.createServer(() => {}).listen(80)
