深入浅出nodejs(第三天)

122 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第二十八天,点击查看活动详情

深入浅出nodejs(第三天)

nodejs 调用c++和c

在node.js中,除了用js写代码以外,还可以使用C++编写扩展,这有点类似DLL,动态链接进js代码中。使用上也相当方便,只需用require包含,这和一般的js模块并没有什么区别。C++扩展为js和C++代码的通信提供了一个接口。在此之前我们需要了解一些定义,例如V8,libuv,node.js内部lib,静态链接库

V8

这里主要再说下Google Chrome,提到Chrome浏览器,一般人会认为使用的Webkit内核,这种说法不完全准确。Chrome发布于2008年,使用的渲染内核是Chromium,它是fork自Webkit,但把Webkit梳理得更有条理可读性更高,效率提升明显。2013年,由于Webkit2和Chromium在沙箱设计上的冲突,谷歌联手Opera自研和发布了Blink引擎,逐步脱离了Webkit的影响。所以,可以这么认为:Chromium扩展自Webkit止于Webkit2,其后Chrome切换到了Blink引擎。另外,Chrome的JS引擎使用的V8引擎,应该算是最著名和优秀的开源JS引擎,大名鼎鼎的Node.js就是选用V8作为底层架构。它实际上是一个C++类库,用来和 JavaScript 交互,比如创建对象,调用函数等等。V8的API大部分都声明在v8.h头文件中。

libuv

主线程将所有任务都放在循环队列中,
然后由底层的libuv库从循环事件队列中取出任务分配给不同的线程去处理,
主线程同时也会进行回调处理,整个过程形成事件循环

nodejs实现异步机制的核心便是libuv,libuv承担着nodejs与文件、网络等异步任务的沟通桥梁

通过事件驱动模型实现了高并发和异步 I/O ,适合处理I/O密集型任务

node.js内部lib

node.js内部lib,node.js本身提供了很多C/C++ API来给扩展使用,比如最重要的一个:node::ObjectWrap类。

静态链接库

node.js包含了很多静态链接库,比如OpenSSL。这些库都放在node.js代码树的deps/目录下。只有V8和OpenSSL标识符被有意地被node.js重复导出来被各种扩展使用。

简单实例

我们先创建hello.cc

image.png

  1. 函数Method的参数类型是FunctionCallbackInfo&,FunctionCallbackInfo

  2. Isolate,英文意思是“隔离”,在这里Isolate指的是一个独立的V8 runtime,可以理解为一个独立的V8执行环境,它包括了自己的堆管理器、GC等组件。后续的很多操作都要依赖于这个Isolate,后面我们会看到在很多操作中,都会使用Isolate的实例作为一个上下文传入。

(注:一个给定的Isolate在同一时间只能被一个线程访问,但如果有多个不同的Isolate,就可以给多个线程同时访问。不过,一个Isolate还不足以运行脚本,你还需要一个全局对象,一个执行上下文通过指定一个全局对象来定义一个完整的脚本执行环境。因此,可以有多个执行上下文存在于一个Isolate中,而且它们还可以简单安全地共享它们的全局对象。这是因为这个全局对象实际上属于Isolate,而却这个全局对象被Isolate的互斥锁保护着。)

3. 返回值需要用args.GetReturnValue().Set()来设置
4. 向外导出方法需要在扩展的初始化函数中使用NODE_SET_METHOD(exports, Method_Name, Method);。如果有多个方法需要导出,就写多个NODE_SET_METHOD

注意到node.js的C++扩展都必须按以下形式导出一个初始化函数(该函数名字可以随便设置一个):

void Initialize(Local exports);
NODE_MODULE(module_name, Initialize)

NODE_MODULE这行后面并没有分号(;) ,因为它并不是一个函数,你可以认为这是一个声明。module_name必须匹配最后生成的二进制文件的文件名(不包括.node后缀)。在hello.cc这个例子中,初始化函数是init,扩展模块名是addon。

构建

node-gyp build

来生成一个编译过的addon.node文件,这个文件会被放在build/Release/目录下。

build成功后,这个二进制的C++扩展就可以在node.js中使用require包含进来:

直接调用即可

const addon = require(``'./build/Release/addon'``);
console.log(addon.hello()) //hello