1. 程式人生 > >node 模組載入原理【1】

node 模組載入原理【1】

[TOC] ## 簡單介紹 我們會從簡單的模組載入原理來開始,嘗試閱讀下 Node.js 原始碼。首先我們知道 Node.js 的原始碼主要是由 C++ 和 JavaScript 編寫的,JS 部分主要在 lib 目錄下,而 C++ 部分主要在 src 目錄下。 ![](https://img2020.cnblogs.com/blog/1004427/202003/1004427-20200329151921980-987119585.png) 模組載入主要是分四種類型的模組: 1. C++ 核心模組:主要在 src 目錄下,比如 node_file.cc 2. Node.js 內部模組:和 C++ 核心模組不同,在原始碼的 lib 目錄下,以同名的 JS 原始碼來實現,實際上 Node.js 內建模組是對 C++ 核心模組的更高層次的封裝,比如 fs、http 3. 使用者原始碼模組 4. C++ 擴充套件模組:外掛是用 C++ 編寫的動態連結共享物件。 require() 函式可以將外掛載入為普通的 Node.js 模組。 外掛提供了 JavaScript 和 C/C++ 庫之間的介面。 這次介紹的是 C++ 核心模組載入的原始碼分析 ## C++ 核心模組載入過程 一句話概括:每個 C++ 核心模組原始碼末尾都有一個巨集呼叫,將模組註冊到 C++ 核心模組的連結串列當中去,以供 `internalBinding` 獲取它。 從這句話可以得到兩個步驟:註冊和獲取。 ### 總的流程 ![](https://img2020.cnblogs.com/blog/1004427/202003/1004427-20200329152207598-817500759.png) 1. 入口檔案執行 我們知道,Node.js 的執行入口在 node.cc 的 Start 函式 ```c++ int Start(int argc, char** argv) {} ``` 2. 分支一:C++ 模組註冊到連結串列中 我們來看看 Start 函式都做了什麼,首先呼叫了 InitializeOncePerProcess,對 Node.js 和 V8 做初始化處理。從這個函式找下去,可以找到 C++ 模組註冊到連結串列 ```c++ InitializationResult result = InitializeOncePerProcess(argc, argv); ``` 3. 分支二:獲取 C++ 模組 在 Start 函式繼續找下去,可以看到例項化了 NodeMainInstance,呼叫了 Run 函式。從這個函式找下去,可以找到獲取 C++ 模組的過程 ```c++ NodeMainInstance main_instance(...); result.exit_code = main_instance.Run(); ``` ### 分支一:C++ 模組註冊到連結串列中 ![](https://img2020.cnblogs.com/blog/1004427/202003/1004427-20200329152336141-625291399.png) #### 1、前置鏈路 從上面的 `InitializeOncePerProcess` 開始往下找,可以看到呼叫了初始化 node 的函式 `InitializeNodeWithArgs` 函式 ```c++ result.exit_code = InitializeNodeWithArgs(&(result.args), &(result.exec_args), &errors); ``` 繼續找,看到它呼叫了 node_binding.cc 中的 `RegisterBuiltinModules`,作用是註冊 C++ 模組 ```c++ binding::RegisterBuiltinModules(); ``` #### 2、C++ 模組註冊函式的呼叫 那我們就來看一下,`RegisterBuiltinModules` 這個函式都做了什麼,主要做了兩件事:一是定義了 `V` 這個巨集,它接收 `modname` 這個引數;二是呼叫 `NODE_BUILTIN_MODULES` ```c++ void RegisterBuiltinModules() { #define V(modname) _register_##modname(); NODE_BUILTIN_MODULES(V) // module name 可以在這個巨集找到,這裡不多寫了 #undef V } ``` 然後我們可以看到 `NODE_BUILTIN_MODULES` 其實也是一個巨集定義: ```c++ #define NODE_BUILTIN_MODULES(V) \ NODE_BUILTIN_STANDARD_MODULES(V) \ ... ``` 其中 `NODE_BUILTIN_STANDARD_MODULES` 的定義是這樣的: ```c++ #define NODE_BUILTIN_STANDARD_MODULES(V) \ V(async_wrap) \ V(buffer) \ ... ``` 也就是說,c++的預處理後,會變成下面的函式體 ```c++ void RegisterBuiltinModules() { _register_async_wrap(); _register_buffer(); .... } ``` 也就是說,最終去呼叫的是 `_register_async_wrap` 和 `_register_buffer` ...這些函式,好了,那麼這些函式是在哪定義的呢? #### 3、C++ 模組註冊函式的定義 從上面巨集定義後面,可以找到這樣的註釋: The definitions are in each module's implementation when calling the NODE_MODULE_CONTEXT_AWARE_INTERNAL. 也就是說,定義是在每個模組呼叫 `NODE_MODULE_CONTEXT_AWARE_INTERNAL` 時進行的~ 好的於是我們看看 fs 模組對應的 C++ 檔案 node_file.cc ,看到最後一行呼叫了 `NODE_MODULE_CONTEXT_AWARE_INTERNAL` ```c++ NODE_MODULE_CONTEXT_AWARE_INTERNAL(fs, node::fs::Initialize) ``` 繼續,在 node_binding.h 看到它呼叫了 `NODE_MODULE_CONTEXT_AWARE_CPP` ```c++ #define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc) \ NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL) ``` 繼續,在 node_binding.h 中,定義了` _register_##modname` 執行時呼叫了 `node_module_register` ```c++ #define NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags) \ void _register_##modname() { node_module_register(&_module); } ``` 好的最後一步,在 node_binding.cc 中找到了它的定義,將傳入的 node module 插到了連結串列 `modlist_internal` 中,後面查詢時就會在這裡找了~ ```c++ extern "C" void node_module_register(void* m) { struct node_module* mp = reinterp