惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
Scott Helme
Scott Helme
爱范儿
爱范儿
WordPress大学
WordPress大学
博客园 - 三生石上(FineUI控件)
阮一峰的网络日志
阮一峰的网络日志
博客园 - Franky
V
V2EX
腾讯CDC
博客园_首页
博客园 - 司徒正美
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tailwind CSS Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
小众软件
小众软件
J
Java Code Geeks
大猫的无限游戏
大猫的无限游戏
月光博客
月光博客
Microsoft Azure Blog
Microsoft Azure Blog
B
Blog
雷峰网
雷峰网
Stack Overflow Blog
Stack Overflow Blog
IT之家
IT之家
罗磊的独立博客
Recorded Future
Recorded Future
博客园 - 聂微东
O
OpenAI News
S
Secure Thoughts
Hacker News: Ask HN
Hacker News: Ask HN
S
Schneier on Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
Y
Y Combinator Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Project Zero
Project Zero
宝玉的分享
宝玉的分享
K
Kaspersky official blog
N
Netflix TechBlog - Medium
T
The Exploit Database - CXSecurity.com
Google Online Security Blog
Google Online Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
Webroot Blog
Webroot Blog
云风的 BLOG
云风的 BLOG
Simon Willison's Weblog
Simon Willison's Weblog
C
Check Point Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
L
LINUX DO - 热门话题
美团技术团队
L
Lohrmann on Cybersecurity

博客园 - gAmesaceR

cocos 3.16 生成预编译库模板 app崩溃后自动重启 安装Jenkins Sublime发布Markdown博客 Markdown Example 关于测试驱动的学习 关于领域驱动的学习 (zt)Lua的多任务机制——协程(coroutine) (zt)IOS开发常用的开源类库和一些示例 (zt)过程生成 [zt][cocos2d-x-win32] 安装部署流程整理 (转发)iOS使用WebGL (转发)XCode实现WebSocket (ZT)UIImage应用与内存管理 (ZT)为什么iPad版FaceBook值得我们苦苦等待 (ZT)关于IAP防止破解的几点 (ZT)iPhone为什么比Android好 在Mac上发布QT的程序 斯坦福大学公开课 iPhone开发教程
Node.js源码研究之模块组织加载
gAmesaceR · 2012-05-07 · via 博客园 - gAmesaceR

粗略研究了一下node.js源码,它有8000行C++代码,2000行javascript代码,来看看js和C++间是怎么组织连接起来,以及各个模块是怎样互相调用的。

本文使用的node.js版本是0.4.8,可以在https://github.com/joyent/node/tree/v0.4.8这里看到源码。

js2c.py

node.js使用了V8附带的js2c.py工具把所有内置的js代码转换成C++里的数组,生成node_natives.h直接include到程序中,成了C++源码的一部分。这样做能提高内置js模块的编译效率。

node.js里内置的javascript包括了主程序src/node.js和模块程序lib/*.js,通过js2c.py让每一个js文件都生成一个源码数组,存放在build/src/node_natives.h里,node_natives.h在node.js编译后才会生成(编译的脚本wscript中调用了js2c.py),可以看到大致的结构如下:

  1. namespace node {  
  2. const char node_native[] = {47, 47, 32, 67, 112 ......}  
  3. const char console_native[] = {47, 47, 32, 67, 112 ......}  
  4. const char buffer_native[] = {47, 47, 32, 67, 112 ......}  
  5. .....  
  6. }  
  7. struct _native { const char* name; const char* source; size_t source_len;};  
  8. static const struct _native natives[] = {  
  9. "node", node_native, sizeof(node_native)-1 },  
  10. "dgram", dgram_native, sizeof(dgram_native)-1 },  
  11. "console", console_native, sizeof(console_native)-1 },  
  12. "buffer", buffer_native, sizeof(buffer_native)-1 },  
  13. ....  

这个文件被包含在node_javascript.cc里,node_javascript.cc提供了两个接口:

MainSource() 处理node_native源码返回v8::Handle类型的数据可供编译。

DefineJavaScript(target) 把其他所有模块源码变成v8::Handle类型后加载到传入的target对象上。

所有的js模块都被转换成了C数组,接下来看看它们怎么执行和互相调用。

执行js主程序/传递process

先看看node.js的底层C++传递给javascript的一个变量process,在一开始运行node.js时,程序会先配置好process

  1. Handleprocess = SetupProcessObject(argc, argv); 

然后把process作为参数去调用js主程序src/node.js返回的函数,这样process就传递到javascript里了。

  1.  
  2.  
  3. Local f_value = ExecuteString(MainSource(), IMMUTABLE_STRING("node.js"));  
  4.  
  5.  
  6.  
  7.  
  8.  
  9.  
  10.  
  11. Local f = Local::Cast(f_value);  
  12.  
  13.  
  14.  
  15. Localglobal = v8::Context::GetCurrent()->Global();  
  16. Local args[1] = { Local::New(process) };  
  17. f->Call(global, 1, args); 

C++模块

node.js的模块除了lib/*.js里用js语言编写的以外,还有一些使用C++编写,像os/stdio/crypto/buffer等。这些模块都通过node.h提供的NODE_MODULE方法存储在变量_module里。node_extensions.cc提供了get_builtin_module(name)接口获取这些模块。

process.binding/C++模块加载

process提供的一个获取模块的接口是binding,它的实现Binding()函数可以在node.cc找到。

  1. Persistent binding_cache;  
  2. static Handle Binding(const Arguments& args) {  
  3. HandleScope scope;  
  4. Local module = args[0]->ToString();  
  5. String::Utf8Value module_v(module);  
  6. node_module_struct* modp;  
  7.  
  8. if (binding_cache.IsEmpty()) {  
  9. binding_cache = Persistent::New(Object::New());  
  10. }  
  11. Local exports;  
  12. if (binding_cache->Has(module)) {  
  13. exports = binding_cache->Get(module)->ToObject();  
  14.  
  15. else if ((modp = get_builtin_module(*module_v)) != NULL) {  
  16. exports = Object::New();  
  17. modp->register_func(exports);  
  18. binding_cache->Set(module, exports);  
  19.  
  20. else if (!strcmp(*module_v, "constants")) {  
  21. exports = Object::New();  
  22. DefineConstants(exports);  
  23. binding_cache->Set(module, exports);  
  24.  
  25. #ifdef __POSIX__  
  26. else if (!strcmp(*module_v, "io_watcher")) {  
  27. exports = Object::New();  
  28. IOWatcher::Initialize(exports);  
  29. binding_cache->Set(module, exports);  
  30. #endif  
  31.  
  32. else if (!strcmp(*module_v, "natives")) {  
  33. exports = Object::New();  
  34. DefineJavaScript(exports);  
  35. binding_cache->Set(module, exports);  
  36.  
  37.  
  38. else {  
  39. return ThrowException(Exception::Error(String::New("No such module")));  
  40. }  
  41. return scope.Close(exports);  

从源码可以看到,调用process.binding时,先看缓存里是否已经存在此模块,不存在再调用get_builtin_module查找C++内置模块,找到的话获取后绑定在exports上,在最后返回exports。

此外还有针对其他模块的特殊处理,其中natives模块就是调用上文提到的DefineJavaScript(exports)接口获取到所有内置的js模块绑定在exports上。

现在在js上需要调用C++提供的模块只需要调用process.binding就行了,例如

  1. var stdio = process.binding("stdio"

js模块加载

src/node.js上实现了一个NativeModule对象用于管理js模块,它通过调用process.binding(“natives”)把所有内置的js模块放在NativeModule._source上,并提供require接口供调用。在require里会给代码加一层包装,把一些变量传给这个模块。

  1. NativeModule.wrapper = [  
  2. '(function (exports, require, module, __filename, __dirname) { ',  
  3. '\n});' 
  4. ]; 

再用process提供的其中一个js编译接口process.runInThisContext执行代码。

  1. var fn = runInThisContext(source, this.filename, true);  
  2. fn(this.exports, NativeModule.require, thisthis.filename); 

于是在主程序src/node.js上可以调用NativeModule.require(“net”)去加载net模块,在lib/*.js的各个js模块里能通过调用传进来的require()去加载其他内置js模块。

总结流程

粗略总结一下加载模块的流程:

加载C++模块(以stdio为例):

process.binding(“stdio”) -> get_builtin_module(“stdio”) -> _module -> NODE_MODULE(node_stdio, node::Stdio::Initialize)(定义)

加载js模块(以net为例)

require(“net”) -> NativeModule.require(“net”) -> process.binding(“natives”)["net"] -> DefineJavaScript() -> natives[] -> node_natives.h

原文:http://cnodejs.org/blog/?p=1280