nodejsモジュールロード分析(1)

6394 ワード

前言
上編のnodejs起動フローの分析には、いくつかの問題が残されています.この記事は主にモジュールロードの流れを説明します.皆さんはtimerモジュールの関連機能を熟知すべきです.私たちはtimerをきっかけにして、一歩ずつ見ていきましょう.
C++init方法開始
下記の関数は全部src/node.ccにあります.
void Init(std::vector<:string>* argv,
          std::vector<:string>* exec_argv) {
  ...
  //       。
  RegisterBuiltinModules();
  ...
}
一見して、このレジスターBultin Modules方法が鍵となります.彼は何ですか?
void RegisterBuiltinModules() {
#define V(modname) _register_##modname();
  NODE_BUILTIN_MODULES(V)
#undef V
}
もともとは方法ではなく、マクロ定義です.じゃ、私達はNODE_を見てみます.BUILTIN_MODULESこれはなんですか?下記の書類はsrc/node_にあります.internals.hにおいて:
#define NODE_BUILTIN_MODULES(V)                                               \
  NODE_BUILTIN_STANDARD_MODULES(V)                                            \
  NODE_BUILTIN_OPENSSL_MODULES(V)                                             \
  NODE_BUILTIN_ICU_MODULES(V)
お利口さん、またマクロです.まるでマクロの地獄です.我慢して、また下を見てみましょう.
#define NODE_BUILTIN_STANDARD_MODULES(V)                                      \
    V(async_wrap)                                                             \
    ...
    V(timer)                                                                 \
他の2つのマクロは似ていますので、載せません.このマクロの中には、探したいタイマーがいるようです.この三つのマクロを合成してみましょう.何かを見てみましょう.
_register_timer();
他の類似点については、これ以上説明しない.このマクロは順番に呼び出されました.レジスターxxの方法.C++マクロの中に、奄菗は文字列接続を表します.〹〹の前の文字列で〹をつづり合わせた後の文字列に相当します.これを見つけました.レジスターtimerの方法が鍵です.全文検索しますレジスターtimer()メソッドは、見つけられないことに気づきました.レジスターというキーワードを検索してみたら、次のようなマクロが見つかります.internals.hにおいて:
#define NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, priv, flags)          \
  static node::node_module _module = {                                        \
    NODE_MODULE_VERSION,   /*   */                                          \
    flags,                 /*    ,builtin,internal,linked*/                \
    nullptr,               /*nm_dso_handle.   */                             \
    __FILE__,              /*   */                                          \
    nullptr,               /*    */                                         \
    (node::addon_context_register_func) (regfunc),    /*     */            \
    NODE_STRINGIFY(modname),          /*   */                               \
    priv,                             /*  */                                 \
    nullptr                           /*    node_module     */            \
  };                                                                          \
  void _register_ ## modname() {                                              \
    node_module_register(&_module);                                           \
  }
見ることができます.このマクロは構造体、node_を定義しています.moduleはsrc/node.hで定義されています.このマクロはモジュール名、登録方法、プライベートポインタ、モジュールタイプを転送します.そして一つの方法を定義しました.私たちが探しているのです.レギター.modnameメソッド.その中でまたmode_を呼び出しました.module_レジスターの方法.ここではまず実現を急ぐのではなく、誰がこのマクロを呼び出したのかを先に見に行きます.検索したら、ファイルのsrc/node_にあります.internals.hには、このようなマクロがある.
#define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc)                  \
  NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL)
NODE_MODULE_CONTEXT_AWARE_INTERNALというマクロは、上のマクロを呼び出し、モジュール名、登録方法を転送し、プライベート変数null、モジュールタイプを内蔵モジュールタイプに転送します.次に誰がNODE_を呼んだかを確認します.MODULE_CONTEXT_AWARE_INTERNAL.検索は多くの場所で呼び出すことができます.私たちは関心のあるSrc/timers.ccファイルを見つけました.
void Initialize(Local target,
                       Local unused,
                       Local context) {
  ...
  env->SetMethod(target, "getLibuvNow", GetLibuvNow);
  ...
  target->Set(env->context(),
              FIXED_ONE_BYTE_STRING(env->isolate(), "immediateInfo"),
              env->immediate_info()->fields().GetJSArray()).FromJust();
}
NODE_MODULE_CONTEXT_AWARE_INTERNAL(timers, node::Initialize)
モジュール名パラメータはtimersであり,初期化方法のパラメータ伝達はInitialize法であることがわかる.ここまではコールの元が見つかりました.じゃ私達は引き続きnode_を見ます.module_レジスターの方法は何をしましたか?この方法はsrc/node.ccにあります.
extern "C" void node_module_register(void* m) {
  struct node_module* mp = reinterpret_cast(m);
  ...
  else if (mp->nm_flags & NM_F_INTERNAL) {
    mp->nm_link = modlist_internal;
    modlist_internal = mp;
  }
  ...
}
ここではすべてのモジュールを順次リンクしてチェーンを作っているだけです.コールがないですか?初期化していませんか?ここでは変数modlist_に注意してください.インターナショナルは後から使います.ここにGetInternal Bindingがあります.彼はglobalオブジェクトにバインドされ、jsに呼び出されます.この方法はjsのソースコードを結合しなければ出典が分かりません.この方法はsrc/node.ccにあります.
static void GetInternalBinding(const FunctionCallbackInfo& args) {
  ...//     。
  Local module = args[0].As();
  ...//      。
  node_module* mod = get_internal_module(*module_v);
  if (mod != nullptr) {
      //    。
    exports = InitModule(env, mod, module);
  }
  ...
}
get_を見せてくださいbuilting.module方法は、この方法はsrc/node.ccにあります.
node_module* get_internal_module(const char* name) {
  return FindModule(modlist_internal, name, NM_F_INTERNAL);
}

inline struct node_module* FindModule(struct node_module* list,
                                      const char* name,
                                      int flag) {
  struct node_module* mp;
  for (mp = list; mp != nullptr; mp = mp->nm_link) {
    if (strcmp(mp->nm_modname, name) == 0)
      break;
  }
  ...
  return mp;
}
メソッド内部でFindModuleメソッドを呼び出しました.最初のパラメータは分かりますか?modlist_インターナショナル上のRegister Buiilting Modules方式で生成されたモジュールです.具体的な検索過程も比較的簡単で、このチェーンを遍歴して、モジュール名を比較して、同じかどうか、そしてモジュールを入手します.InitModuleの方法を引き続き見てください.同じsrc/node.ccにあります.
static Local InitModule(Environment* env,
                                 node_module* mod,
                                 Local module) {
  ...//    node_module           ,          。
  mod->nm_context_register_func(exports,
                                unused,
                                env->context(),
                                mod->nm_priv);
  return exports;
}
上のtimer.ccのInitializeメソッドを思い出してみます.ここが呼び出された場所です.これでモジュールはプログラムにロードされます.
最後の言葉
分析中、多くのマクロ処理があります.マクロを一つずつ展開して、紙に書いて分析します.さもなくば一目でとてもみっともないのは全体の姿を点検します.
本稿を通じて、Register Modulesとどのようにモジュールを初期化するかについて大まかな理解が必要である.でも、GetInternal Bindingという方法は誰が呼び出したのか、今度は分析します.