精华 Node.js挖掘之三:一张图介绍Node.js的各路英雄
发布于 5 年前 作者 LanceHBZhang 16773 次浏览 最后一次编辑是 4 年前 来自 分享

overall

1.png

图1:Node.js overview

本文所做的研究基于Node.js v0.12.4 Linux版本。

熟悉Node.js的同学都知道Node.js由v8 engine,内建的模块以及libuv组成。这是一个宽泛的介绍。本文通过一张图来详细了解以下Node.js运行中涉及到的各路英雄。

对于Node.js而言,可以将v8 engine和libuv看成是一个库。虽然事实上,在编译Node时,二者是以源代码方式直接编译进可执行文件node中去的。v8 engine和libuv源代码放在node-vXXX/deps目录下。 本文先依次介绍v8 engien和libuv,最后结合图1讲述Node.js如何将这二者优雅地结合在一起的。

v8 engine

V8 engine的详细说明可以参考V8的在线文档(https://developers.google.com/v8/intro)。

	#include "include/v8.h"
	#include "include/libplatform/libplatform.h"

	using namespace v8;

	int main(int argc, char* argv[]) {
	// Initialize V8.
	V8::InitializeICU();
	Platform* platform = platform::CreateDefaultPlatform();
	V8::InitializePlatform(platform);
	V8::Initialize();

	// Create a new Isolate and make it the current one.
	Isolate* isolate = Isolate::New();
	{
		Isolate::Scope isolate_scope(isolate);

		// Create a stack-allocated handle scope.
		HandleScope handle_scope(isolate);

		// Create a new context.
		Local<Context> context = Context::New(isolate);
  
		// Enter the context for compiling and running the hello world script.
		Context::Scope context_scope(context);
  
		// Create a string containing the JavaScript source code.
		Local<String> source = String::NewFromUtf8(isolate, "'Hello' + ', World!'");
  
		// Compile the source code.
		Local<Script> script = Script::Compile(source);
  
		// Run the script to get the result.
		Local<Value> result = script->Run();
  
		// Convert the result to an UTF8 string and print it.
		String::Utf8Value utf8(result);
		printf("%s\n", *utf8);
	}

	// Dispose the isolate and tear down V8.
	isolate->Dispose();
	V8::Dispose();
	V8::ShutdownPlatform();
	delete platform;
	return 0;
}

上面是一个典型的v8使用示例。总体而言,v8里面包含几个重要的概念:isolate, context, handles。

  1. isolate代表一个v8 engine 实例。各个isolate之间的状态完全独立,位于一个isolate中的object无法在另外一个isolate中被使用。
  2. context代表一个执行JS代码的虚拟机,在这个虚拟机中已经集成了一些object如Math, JSON以及function如Date(), RegExp(), String(), Object(), and Array()。一个isolate中可以同时存在多个context,且可以在这些context中切换。如图2所示。
  3. 在v8 engine中通过handle访问存在于heap上的JS object。handle有不少种类,包括Local, Persistent, Eternal等。在v8中有一个handle stack用于管理这些handles,如图3所示。

1.png

图2:v8 engine context

1.png

图3: v8 engine handle stack

libuv

libuv是一个多平台的,主要用于处理事件驱动的异步I/O模型库。可以参考http://docs.libuv.org/en/v1.x/ 获取更详细的信息。 1.png

图4: libuv模块分布图

取决于所运行的操作系统平台,libuv通过epoll, IOCP等方式来实现异步network I/O。而对于File I/O, DNS的操作则依赖于Thread Pool来实现。如图4所示。 http://nikhilm.github.io/uvbook/ 是一个非常好的libuv入门以及API参考文档。 libuv的核心部分是I/O (or event) loop。如图5所示,该loop负责调用callback function、运行idle function、执行事件polling轮询等操作。

1.png

图5:libuv I/O(or event) loop

Node.js

介绍完v8 engine和libuv,让我们把目光再次转移到图1上来。除了牵手v8 engine和libuv外,Node.js本身也做了不少工作。提供了包括JS模块,C++模块(包括各种wrap)在内的核心模块供开发者使用。

如果按照Node代码的命名方法,这些JS模块叫“native module”,而C++模块叫“builtin module”。Node.js的代码包括libuv基本上是以C++写成的,将这些用C++写成的模块叫builtin也是合情合理。而native则喻为与Node之生俱来,也与第三方开发的以示区别。

内建的C++模块位于Node源代码的src目录,而JS模块则位于lib目录。通常C++模块是作为内部模块导出给lib/*.js模块使用,如C++所写的tcp_wrap模块是导出给net.js模块使用,而C++所写的crypto则会提供给crypto.js使用。

图6是一个关于native module和builtin module如何编译以及加载到内存的过程。 对于Node自身提供的模块,其实无论是JS模块还是C++模块,最终都在编译生成可执行文件node时,嵌入到了ELF文件里面。而对这两者的提取方式却不一样。对于JS模块,使用process.binding(“natives”),而对于C++模块则直接用get_builtin_module()得到。

1.png

图6:native module和buildin module编译和加载过程

当我们执行 node test.js 执行我们所写的JS代码时,node_main.cc首先被调用。这一步主要的工作是初始化v8 engine和libuv执行环境。 uv_run(env->event_loop(), UV_RUN_ONCE) 用于启动libuv event loop。而我们的JS代码 test.js则会作为输入传递给CreateEnvironment(),最终交由v8 engine执行。

查看Node.cc代码,可以看到若干与v8 engine交互的工作,包括context, handle等的创建、通过v8的函数模版创建类的constructor以及类的prototype object等操作。 其中通过函数模版创建类的constructor以及类的prototype object等操作非常有意思。

在JavaScript中,类是基于prototype继承的方式来实现的。一个JS类包含一个constructor和prototype object以及包括其它如__proto__在内的property。通过v8提供的C++ API来完成这些操作其实是生成与JS的类概念相同的代码。我后面会写一篇文章介绍“callback from libuv to JS”,里面会详细介绍对于一个具体的callback function如何通过这种方式来实现从C++世界到JavaScript世界的穿越。

转载本文请注明作者和出处,请勿用于任何商业用途。如需帮助,请QQ联系作者:229848501

15 回复

突然发现被设成精华帖了。感动死了。谢谢!

你也可以去申请机器人了,哈哈

@luoyjx 谢谢提醒。alsotang已经寄出来啦。

好东西要支持

好东西就应该加精

From Noder

学习了。

楼主说得好深入,但是不懂c++好些地方没看明白

难得的好文章

这图也太小了,里面的字根本看不清啊

回到顶部