得到hello.node结果文件后,如果调用扩展模块其实在前面已经提及。require()方法通过解析标识符、路径分析、文件定位,然后加载执行即可。下面的代码引入前面编译得到的.node文件,并调用执行其中的方法:
~~~
const hello = require("./build/Release/hello.node");
console.log(hello.sayHello());
~~~
以上代码存为hello.js,调用`node hello.js`命名即可得到如下输出结果:
~~~
Hello World!
~~~
对于以.node为扩展名的文件,Node将会调用process.dlopen() 方法去加载文件:
~~~
//Native extension for .node
Module._extensions['.node'] = process.dlopen;
~~~
对于调用者而言,require()是轻松愉快的。对于扩展模块的编写者来说,process.dlopen()中隐含的过程值得了解一番。如图:
![](https://box.kancloud.cn/2016-08-26_57bf1fe42ae1f.png)
require()在引入.node文件的过程中,实际上经历了4个层面上的调用。
加载.node文件实际上经历了两个步骤,第一个步骤是调用uv_dlopen()方法去打开动态链接库,第二个步骤是调用uv_dlsym()方法找到动态链接库中通过NODE_MODULE宏定义的方法地址。这两个过程都是通过libuv库进行封装的:在`*nix`平台下实际上调用的是dlfcn.h头文件中定义的dlopen()和dlsym()两个方法;在Windows平台则是通过LoadLibraryExW()和GetProcAddress()这两个方法实现的,它们分别加载.so和.dll文件(实际为.node文件)。
这里对libuv函数的调用充分表现Node利用libuv实现跨平台的方式,这样的情景在很多地方还会出现。
由于编写模块时通过NODE_MODULE将模块定义为`node_module_struct`结构,所以在获取函数地址之后,将它映射为`node_module_struct`结构几乎是无缝对接的。接下来的过程就是将传入的exports对象作为实参运行,将C++中定义的方法挂载在exports对象上,然后调用者就可以轻松调用了。
C/C++扩展模块与JavaScript模块的区别在于加载之后不需要编译,直接执行之后就可以被外部调用了,其加载速度比JavaScript模块略快。
使用C/C++扩展的一个好处就在于可以灵活和动态的加载它们,保持Node模块自身简单性的同时,给予Node无限的可扩展性。
关于node-gyp工具的更多细节可以参见 https://github.com/TooTallNate/node-gyp(作者为Nathan Rajlich,Node源码的核心贡献者之一)。
- 目录
- 第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 事件驱动与高性能服务器