2.4 C/C++扩展模块

34 阅读2分钟

这一小节是第二章的延伸,朴灵作者在这里介绍当JavaScript性能不足时,如何用C/C++写原生扩展模块(Native Addon)来提升Node的性能。核心模块(如fs、crypto)很多就是用C++实现的,但我们自己也可以写自定义的.node模块。这在2013年书出版时是常见的做法(现在有N-API、更稳定的方式,但原理类似)。

这一小节内容相对实践性强,作者通过一个简单示例演示了从编写C++代码到编译成.node文件,再到在JS中require的过程。适合对性能优化感兴趣的开发者。

详细讲解

为什么需要C/C++扩展模块?

  • JavaScript适合I/O密集,但CPU密集任务(如复杂计算、图像处理)慢。
  • Node允许用C++写addon,暴露给JS调用,结合V8(JS引擎)和libuv(异步I/O)。
  • 扩展模块以**.node**为后缀,像普通模块一样require('mymodule.node')。

扩展模块的加载方式

  • 在路径分析时,如果文件是.node扩展,用process.dlopen加载(类似动态链接库)。
  • 与.js不同,不经过包裹函数,直接执行C++初始化函数。

示例:Hello World扩展

书里用一个简单示例演示(类似V8的Hello World):

  1. C++代码(addon.cc):

    #include <node.h>
    #include <v8.h>
    
    using namespace v8;
    
    void Method(const FunctionCallbackInfo<Value>& args) {
      Isolate* isolate = args.GetIsolate();
      args.GetReturnValue().Set(String::NewFromUtf8(isolate, "hello world").ToLocalChecked());
    }
    
    void init(Local<Object> exports) {
      NODE_SET_METHOD(exports, "hello", Method);
    }
    
    NODE_MODULE(addon, init)  // 关键宏:定义模块名和初始化函数
    
  2. 编译:用binding.gyp(GYP工具,书时Node用这个,现在多用node-gyp + CMake):

    {
      "targets": [{
        "target_name": "addon",
        "sources": ["addon.cc"]
      }]
    }
    

    命令:node-gyp configure build → 生成build/Release/addon.node

  3. JS中使用

    var addon = require('./build/Release/addon');
    console.log(addon.hello()); // 'hello world'
    

关键概念

  • V8 API:操作JS对象(String、Object、Function等)。
  • NODE_MODULE宏:注册模块初始化函数。
  • 数据类型转换:C++和JS之间(如Number、Array)。
  • 线程注意:不能阻塞V8主线程,复杂计算用libuv队列。

注意事项

  • 兼容性:不同Node版本V8不同,addon可能不兼容(现在N-API解决)。
  • 调试难:用gdb或lldb。
  • 性能:适合热点代码。