node源码详解(五)
?本作品采用知識共享署名 4.0 國際許可協(xié)議進行許可。轉載保留聲明頭部與原文鏈接https://luzeshu.com/blog/nodesource5?
本博客同步在https://cnodejs.org/topic/56ed6735b705742136388fa6?
本博客同步在http://www.cnblogs.com/papertree/p/5295344.html
在上一篇博客(詳解四)講了 C++通過v8的Object類和FunctionTemplate類,創(chuàng)建對象、方法,設置屬性、原型方法等,提供給運行時的 js代碼調用。
那么這些C++實現的process對象、TCP類是否都在程序啟動的時候就創(chuàng)建到 js的執(zhí)行環(huán)境(context)呢?
不全是。process對象是(見5.2節(jié)),但 TCP類等C++內建模塊不是(見5.3節(jié))?! ?/p>
5.1 在main函數啟動之前 —— C++內建模塊的注冊
看一下C++內建模塊的實現方法:
圖5-1-1
我們看到最后一行的NODE_MODULE_CONTEXT_AWARE_BUILTIN,通過這個方式來導出一個C++內建模塊,這行代碼實現了什么?
5.1.1?NODE_MODULE_CONTEXT_AWARE_BUILTIN 宏
看下這個宏的實現,在src/node.h文件:
圖5-1-2
5.1.2 NODE_C_CTOR宏與__attribute__((constructor))聲明 —— 在main函數前注冊
看到NODE_C_CTOR這個宏的作用時給傳進來的函數加上這么一個聲明,這是gcc的一個函數屬性聲明。
來自gcc文檔的說明:(地址:https://gcc.gnu.org/onlnedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes)
[?The?constructor?attribute causes the function to be called automatically before execution enters?main (). ]
?[筆者譯:constructor屬性導致該函數在程序進入main函數之前被執(zhí)行]
注:上面的 “.CRT$XCU”是微軟的編譯器實現類似功能的方法。
5.1.3 node_module_register函數 與 node::node_module結構體的鏈表modlist_builtin
通過圖5-1-2,可以知道通過NODE_MODULE_CONTEXT_AWARE_BUILTIN( tcp_wrap, TCPWrap::Initialize )宏去注冊一個C++內建模塊時,過程如下:
1. 把 tcp_wrap(modname)和 Initilize函數(regfunc)封裝成一個node_module類型的結構體 _module
2.?調用node_module_register(&_module) 函數進行注冊。
圖5-1-3
可以看到這個函數把傳進來的m插入到內建模塊的鏈表modlist_builtin。
?
5.1.4 總結
通過5.1節(jié),我們知道tcp_wrap.cc里面實現的C++內建模塊,在main函數啟動之前把該內建模塊的初始化函數 TCPWrap::Initialize()保存到一個全局的靜態(tài)鏈表modlist_builtin。
那么這些內建模塊的Initialize()什么時候執(zhí)行呢?里面可是創(chuàng)建了TCP類對應的FunctionTemplate呢,在創(chuàng)建對應的FunctionTemplate對象之前,V8的context里頭,js代碼還是沒得直接使用new TCP()呢。
5.2 process.binding —— C++內建模塊的初始化與緩存
既然C++內建模塊只是保存初始化函數到鏈表,而不真正創(chuàng)建一個js可以使用的函數對象(TCP類)到v8的上下文(context),那么初始化這個步驟明顯要交給 js代碼來控制了。
通過process.binding('tcp_wrap') 來引入C++內建模塊創(chuàng)建的對象,如果第一次binding這個內建模塊,那么就會調用Initialize函數。
那么你可能會問,C++內建模塊就需要 js里面調用process.binding()來引入,那process對象也是C++提供的v8::Object類型的對象,為什么可以直接使用process.binding()呢?
因為內建模塊并不是你js代碼一定會用到的,所以通過保存TCPWrap::Initialize()的方式,等到要用到時再通過process.binding()初始化。你要一開始就執(zhí)行初始化函數,也是可以創(chuàng)建對應的FunctionTemplate對象到v8上下文的。
而process這個對象在main函數啟動之后、執(zhí)行node.js之前就創(chuàng)建了,那么js代碼運行的上下文(context)里面就可以直接用了。具體代碼見5.3節(jié)。
來看一下process.binding()的過程:
[注:C++設置給process對象的binding方法是下面的Binding()函數]
圖5-2-1
1. Binding()函數通過要binding的模塊名,調用get_builtin_module(),從5.1.3中提到的內建模塊鏈表modlist_buildin去獲取node::node_module結構體對象
2. node_module對象里面的nm_context_register_func字段保存著對應的初始化函數(即5.1.3中保存的TCPWrap::Initialize),圖中第2320行可以看出binding的時候執(zhí)行了初始化函數。
3. 最后把exports當成process.binding()的返回值。exports哪里被賦值呢?注意到nm_context_register_func其實是tcp_wrap.cc里面的TCPWrap::Initialize()函數,回去圖5-1-1看看這個函數,就會發(fā)現哦原來就是target。
?
?
5.3 process對象的初始化
在3.1.2節(jié)中講到這么一個流程:
main() ->?Start() -> StartNodeInstance() -> LoadEnviroment()
在LoadEnvrionment()里面去執(zhí)行node.js文件,并把process對象傳進去。
5.3.1 process初始化(js部分)
在LoadEnvrionment()執(zhí)行node.js文件,并傳process進去的時候,process.nextTick()這些函數是在node.js里面初始化的。
5.3.1 process初始化(C++部分)
在main() -> Start() -> StartNodeInstance() 這里,執(zhí)行LoadEnvironment()之前,先調用了CreateEnvironment()。
在CreateEnvironment()里面創(chuàng)建process對象,并且初始化C++部分的函數??创a:
圖5-3-1
1. 創(chuàng)建v8::Object process_object 在 CreateEnvironment()里面。
2. 給process.binding等賦值在SetupProcessObject()里面。
?
轉載于:https://www.cnblogs.com/xieweikai/p/6817671.html
總結
以上是生活随笔為你收集整理的node源码详解(五)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 逻辑表达式——黑纸白纸
- 下一篇: 51Nod - 1381 硬币游戏