前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >拓展nodejs内核

拓展nodejs内核

作者头像
theanarkh
发布2020-06-15 15:27:57
8240
发布2020-06-15 15:27:57
举报
文章被收录于专栏:原创分享原创分享

拓展nodejs的方式有很多种,可以写npm包,可以写c++插件,还可以修改内核重新编译分发。本文介绍如何通过为nodejs内核增加一个c++模块的方式拓展nodejs的功能(git地址:https://github.com/theanarkh/learn-how-to-extend-node)。相比修改nodejs内核代码,新增一个nodejs内置模块需要了解更多的知识。下面我们开始。 1 首先在src文件夹下新增两个文件。 cyb.h

代码语言:javascript
复制
#ifndef SRC_CYB_H_
#define SRC_CYB_H_

#include "v8.h"

namespace node {

class Environment;

class Cyb {
 public:

  static void Initialize(v8::Local<v8::Object> target,
                         v8::Local<v8::Value> unused,
                         v8::Local<v8::Context> context,
                         void* priv);

 private:

  static void Console(const v8::FunctionCallbackInfo<v8::Value>& args);
};


}  // namespace node

#endif

cyb.cc

代码语言:javascript
复制
#include "cyb.h"
#include "env-inl.h"
#include "util-inl.h"
#include "node_internals.h"

namespace node {

using v8::Context;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::FunctionTemplate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Value;

void Cyb::Initialize(Local<Object> target,
                         Local<Value> unused,
                         Local<Context> context,
                         void* priv) {
  Environment* env = Environment::GetCurrent(context);
  // 申请一个函数模块,模板函数是Console
  Local<FunctionTemplate> t = env->NewFunctionTemplate(Console);
  // 申请一个字符串
  Local<String> str = FIXED_ONE_BYTE_STRING(env->isolate(), "console");
  // 设置函数类名
  t->SetClassName(str);
  // 导出函数,target即exports
  target->Set(env->context(),
              str,
              t->GetFunction(env->context()).ToLocalChecked()).Check();
}

void Cyb::Console(const FunctionCallbackInfo<Value>& args) {
  v8::Isolate* isolate = args.GetIsolate();
  v8::Local<String> str = String::NewFromUtf8(isolate, "hello world");
  args.GetReturnValue().Set(str);
}

}  // namespace node
// 声明该模块
NODE_MODULE_CONTEXT_AWARE_INTERNAL(cyb_wrap, node::Cyb::Initialize)

我们新定义一个模块,是不能自动添加到nodejs内核的。我们还需要额外的操作。我们需要修改node.gyp文件。把我们新增的文件加到配置里,否则编译的时候,不会编译这个新增的模块。我们可以在node.gyp文件中找到src/tcp_wrap.cc,然后在他后面加入我们的文件就行。

代码语言:javascript
复制
src/cyb_wrap.cc
src/cyb_wrap.h

这时候nodejs会编译我们的代码了。但是nodejs的内置模块有一定的机制,我们的代码加入了nodejs内核,不代表就可以使用了。我们看一下nodejs对内置++ 模块的机制。nodejs在初始化的时候会调用RegisterBuiltinModules函数注册所有的内置c++模块。

代码语言:javascript
复制
void RegisterBuiltinModules() {
#define V(modname) _register_##modname();
  NODE_BUILTIN_MODULES(V)
#undef V
}

我们看到该函数只有一个宏。我们看看这个宏。

代码语言:javascript
复制
#define NODE_BUILTIN_MODULES(V)                                                \
  NODE_BUILTIN_STANDARD_MODULES(V)                                             \
  NODE_BUILTIN_OPENSSL_MODULES(V)                                              \
  NODE_BUILTIN_ICU_MODULES(V)                                                  \
  NODE_BUILTIN_REPORT_MODULES(V)                                               \
  NODE_BUILTIN_PROFILER_MODULES(V)                                             \
  NODE_BUILTIN_DTRACE_MODULES(V)   

宏里面又是一堆宏,本文不是源码解析,所以不深入讲解,可以参考之前的文章。我们要做的就是修改这个宏。因为我们是自定义的内置模块,所以我们可以增加一个宏。

代码语言:javascript
复制
#define NODE_BUILTIN_EXTEND_MODULES(V)                                       \
  V(cyb_wrap) 

然后把这个宏追加到那一堆宏后面。

代码语言:javascript
复制
#define NODE_BUILTIN_MODULES(V)                                                \
  NODE_BUILTIN_STANDARD_MODULES(V)                                             \
  NODE_BUILTIN_OPENSSL_MODULES(V)                                              \
  NODE_BUILTIN_ICU_MODULES(V)                                                  \
  NODE_BUILTIN_REPORT_MODULES(V)                                               \
  NODE_BUILTIN_PROFILER_MODULES(V)                                             \
  NODE_BUILTIN_DTRACE_MODULES(V)                                               \
  NODE_BUILTIN_EXTEND_MODULES(V)

这时候,nodejs不仅可以编译我们的代码,还把我们的代码定义的模块注册到内置c++模块里了。接下来就是如何使用c++模块了。 2 在lib文件夹新建一个cyb.js,作为nodejs原生模块

代码语言:javascript
复制
const cyb = internalBinding('cyb_wrap'); 
module.exports = cyb;

internalBinding函数是在执行cyb.js,注入的函数。不能在用户js里使用。internalBinding函数就是根据模块名从内置模块里找到对应的模块。即我们的cyb.cc。新增原生模块,我们也需要修改node.gyp文件,否则代码也不会被编译进node内核。我们找到node.gyp文件的lib/net.js。在后面追加lib/cyb.js。该配置下的文件是给js2c.py使用的。如果不修改,我们在require的时候,就会找不到该模块。最后我们在lib/internal/bootstrap/loader文件里找到internalBindingWhitelist变量,在数组最后增加cyb_wrap。这个配置是给process.binding函数使用的,如果不修改这个配置,通过process.binding就找不到我们的模块。process.binding是可以在用户js里使用的。

到此,我们完成了所有的修改工作,重新编译nodejs。然后编写测试程序。 3 新建一个测试文件testcyb.js

代码语言:javascript
复制
// const cyb = process.binding('cyb_wrap');
const cyb = require('cyb'); 
console.log(cyb.console())

输出hello world。

总结:本文介绍如何给nodejs内核新增一个新的模块,学习修改nodejs内核是成为nodejs contributor的第一步,结合v8和libuv的知识。你可以为nodejs做得更多,

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档