在核心模塊中,有些模塊全部由C/C++編寫,有些模塊則由C/C++完成核心部分,其它部分則由JavaScript實現包裝或向外導出,以滿足性能需求。后面這種C++模塊主內完成核心,JavaScrpt主外實現封裝的模式是Node能夠提高性能的常見方式。通常,腳本語言的開發速度優于靜態語言,但是其性能則弱于靜態語言。而Node的這種復合模式可以在開發速度和性能之間找到平衡點。
這里我們將那些由純C/C++編寫的部分統一稱為內建模塊,因為它們通常不被用戶直接調用。Node的buffer、crypto、evals、fs、os等模塊都是部分通過C/C++編寫的。
## 1.內建模塊的組織形式
在Node中,內建模塊的內部結構定義如下:
~~~
struct node_module_struct{
int version;
void *dso_handle;
const char *filename;
void (*register_func) (v8::Handle<v8::Object> traget);
const char *modname;
};
~~~
每一個內建模塊在定義之后,都通過`NODE_MODULE`宏將模塊定義到 `node` 命名空間中,模塊的具體初始化方法掛載為結構的` register_func`成員:
~~~
#define NODE_MODULE(modname, regfunc) \
extern "C" { \
NODE_MODULE_EXPORT node::node_module_struct modname ## _module = \
{ \
NODE_STANDARD_MODULE_STUFF, \
regfunc, \
NODE_STRINGIFY(modname) \
}; \
} \
~~~
`node_extensions.h`文件將這些散列的內建模塊統一放進了一個叫`node_module_list`的數組中,這些模塊有:
* node_buffer
* node_crypto
* node_evals
* node_fs
* node_http_parser
* node_os
* node_zlib
* node_timer_wrap
* node_tcp_wrap
* node_udp_wrap
* node_pipe_wrap
* node_cares_wrap
* node_tty_wrap
* node_process_wrap
* node_fs_event_wrap
* node_signal_watcher
這些內建模塊的取出也十分簡單。Node提供了`get_builtin_module()`方法從`node_module_list`數組中取出這些模塊。
內建模塊的優勢在于:首先,它們本身由C/C++編寫,性能行優于腳本語言;其次,在進行文件編譯時,它們被編譯進二進制文件。一旦Node開始執行,它們被直接加載進內存中,無需再次做標識符定位、文件定位、編譯等過程,直接就可執行。
## 2.內建模塊的導出
在Node的所有模塊類型中,存在著如下圖所示的一種依賴層級關系,即文件模塊可能會依賴核心模塊,核心模塊可能會依賴內建模塊。

通常,不推薦文件模塊直接調用內建模塊。如需調用,直接調用核心模塊即可,因為核心模塊中基本都封裝了內建模塊。那么內建模塊是如何將內部變量或方法導出,以供外部JavaScript核心模塊調用呢?
Node在啟動時,會生成一個全局變量process,并提供Binding()方法來協助加載內建模塊。 Binding()的實現代碼在src/node.cc中,具體如下所示:
~~~
static Handle<Value> Binding(const Arguments& args){
HandleScope scope;
Local<String> module = args[0]->ToString();
String::Utf8Value module_v(module);
node_module_struct* modp;
if(binding_cache.IsEmpty()){
binding_cache=Presistent<Object>::New(Object::New());
}
Local<Object> exports;
if(binding_cache->Has(module)){
exports = binding_cache->Get(module)->ToObject();
return scope.Close(exports);
}
// Append a string to process.moduleLoadList
char buf[1024];
snprintf(buf, 1024, "Binding %s", *module_v);
uint32_t l = module_load_list->Length();
module_load_list->Set(l, String::New(buf));
if((modp=get_builtin_module(*module_v)) != NULL){
exports = Object::New();
modp->register_func(exports);
binding_cache->Set(module, exports);
}else if(!strcmp(*module_v,"constants")){
exports = Object::New();
DefineConstants(exports);
binding_cache->Set(module,exports);
#ifdef __POSIX__
}else if(!strcmp(*module_v,"io_watcher")){
exports = Object::New();
IOMatcher::Initialize(exports);
binding_cache->Set(module, exports);
#endif
}else if (!strcmp(*module_v,"natives")){
exports = Object::New();
DefineJavaScript(exports);
binding_cache->Set(module, exports);
}else{
return ThrowException(Exception::Error(String::New("No such module")));
}
return scope.Close(exports);
}
~~~
在加載內建模塊時,我們先創建一個exports空對象,然后調用get_builtin_module()方法取出內建模塊對象,通過執行register_func()填充exports對象,最后將exports對象按模塊名緩存,并返回給調用方完成導出。
這個方法不僅可以導出內建方法,還能導出一些別的內容。前面提到的JavaScript核心文件被轉換為C/C++數組存儲后,便是通過process.binding('natives')取出放置在NativeModule._source中的:
~~~
NativeModule._source = process.binding('natives');
~~~
該方法將通過js2c.py工具轉換出的字符串數組取出,然后重新轉換為普通字符串,以對JavaScript核心模塊進行編譯和執行。
- 目錄
- 第1章 Node 簡介
- 1.1 Node 的誕生歷程
- 1.2 Node 的命名與起源
- 1.2.1 為什么是 JavaScript
- 1.2.2 為什么叫 Node
- 1.3 Node給JavaScript帶來的意義
- 1.4 Node 的特點
- 1.4.1 異步 I/O
- 1.4.2 事件與回調函數
- 1.4.3 單線程
- 1.4.4 跨平臺
- 1.5 Node 的應用場景
- 1.5.1 I/O 密集型
- 1.5.2 是否不擅長CPU密集型業務
- 1.5.3 與遺留系統和平共處
- 1.5.4 分布式應用
- 1.6 Node 的使用者
- 1.7 參考資源
- 第2章 模塊機制
- 2.1 CommonJS 規范
- 2.1.1 CommonJS 的出發點
- 2.1.2 CommonJS 的模塊規范
- 2.2 Node 的模塊實現
- 2.2.1 優先從緩存加載
- 2.2.2 路徑分析和文件定位
- 2.2.3 模塊編譯
- 2.3 核心模塊
- 2.3.1 JavaScript核心模塊的編譯過程
- 2.3.2 C/C++核心模塊的編譯過程
- 2.3.3 核心模塊的引入流程
- 2.3.4 編寫核心模塊
- 2.4 C/C++擴展模塊
- 2.4.1 前提條件
- 2.4.2 C/C++擴展模塊的編寫
- 2.4.3 C/C++擴展模塊的編譯
- 2.4.2 C/C++擴展模塊的加載
- 2.5 模塊調用棧
- 2.6 包與NPM
- 2.6.1 包結構
- 2.6.2 包描述文件與NPM
- 2.6.3 NPM常用功能
- 2.6.4 局域NPM
- 2.6.5 NPM潛在問題
- 2.7 前后端共用模塊
- 2.7.1 模塊的側重點
- 2.7.2 AMD規范
- 2.7.3 CMD規范
- 2.7.4 兼容多種模塊規范
- 2.8 總結
- 2.9 參考資源
- 第3章 異步I/O
- 3.1 為什么要異步I/O
- 3.1.1 用戶體驗
- 3.1.2 資源分配
- 3.2 異步I/O實現現狀
- 3.2.1 異步I/O與非阻塞I/O
- 3.2.2 理想的非阻塞異步I/O
- 3.2.3 現實的異步I/O
- 3.3 Node的異步I/O
- 3.3.1 事件循環
- 3.3.2 觀察者
- 3.3.3 請求對象
- 3.3.4 執行回調
- 3.3.5 小結
- 3.4 非I/O的異步API
- 3.4.1 定時器
- 3.5 事件驅動與高性能服務器