Как nodejs работает с libuv и v8? (В конце статьи есть пасхалки)

Node.js

В настоящее время в этой статье используется версия nodejs v13.1.0 (все статьи в Интернете, которые анализируют исходный код nodejs без написания версии на его основе, являются хулиганами), очень сухая статья, пожалуйста, прочитайте ее терпеливо, в противном случае рекомендуется собирать

Пожалуйста, прочитайте предыдущие статьи, прежде чем читать эту статью:

Прочитав эту статью, вы узнаете:

  • процесс запуска nodejs
  • Классификация модулей nodejs и соответствующих процессов и принципов их загрузки
  • Принцип js-кода в nodejs, вызывающего функцию C++
  • Дополнительные вопросы на собеседовании

1. От чего зависит Nodejs?

Прежде всего, nodejs предоставляет так много модулей и может работать на разных платформах не потому, что js великолепен, а потому, что нижний уровень полагается на некоторые технологии, которые вы не знаете. Две самые большие зависимости — это v8 и libuv. Почему ты это сказал? Потому что один помогает конвертировать код js в машинный код, который может работать на различных платформах и машинах, а другой помогает вызывать различные системные функции на платформах и машинах, включая рабочие файлы, мониторинг сокетов и так далее. Оставив в стороне эти две самые большие зависимости, давайте взглянем на исходный код nodejs.depsЧто в каталоге?

На картинке выше показан пакет, от которого зависит Nodejs.На официальном сайте мы можем найти введение некоторых зависимых пакетов:Dependencies

  1. http_parser: Как следует из названия, это анализатор HTTP, облегченный анализатор, написанный на языке C. Поскольку синтаксический анализатор спроектирован так, чтобы не выполнять никаких системных вызовов или выделений памяти, объем памяти, занимаемой каждым запросом, очень мал.
  2. c-ares: для некоторого асинхронного разрешения DNS nodejs использует эту библиотеку C. То, что выставлено на уровне js, — это модуль DNS.resolve()семейная функция.
  3. OpenSSL: OpenSSL широко используется как в модулях tls, так и в модулях шифрования. Он предоставляет хорошо протестированные реализации многих криптографических функций, на которые опирается современная сеть для обеспечения безопасности.
  4. zlib: для быстрого сжатия и распаковки Node.js использует стандартную библиотеку zlib, также известную своим использованием в gzip и libpng. Nodejs использует zlib для создания интерфейсов синхронного, асинхронного или потокового сжатия и распаковки.
  5. npm: Не буду вдаваться в подробности

Несколько других, которые не упоминаются на официальном сайте, также упоминаются здесь:

  1. acorn: небольшой, но эффективный парсер javascript.
  2. acorn-plugins: некоторые плагины, используемые acorn.С точки зрения имени, эта версия Nodejs поддерживает функции bigInt, функции частного класса и метода и т. д.
  3. brotli: Предоставляет версию реализации алгоритма сжатия Brotli на языке C.
  4. histogram: Версия на языке C реализует гистограмму с высоким динамическим диапазоном.Прочитав введение, я не знаю, почему nodejs должен ссылаться на это?
  5. icu: ICU (International Components for Unicode) — это набор зрелых и широко используемых библиотек C/C++ и Java, обеспечивающих поддержку Unicode и глобализации для программных приложений.
  6. llhttp: более производительный и удобный в сопровождении парсер http.
  7. nghttp2: Реализация протокола HTTP/2 на языке C, алгоритм сжатия заголовков используетHPACK
  8. node-inspect: Библиотека пытается быть доступной в новой версии V8.node debugЗаказ.
  9. uv: Одна из сущностей Nodejs, предоставляющая Nodejs возможность доступа к различным функциям операционной системы, включая файловую систему, Socket и т. д.
  10. v8: Скомпилируйте код Js в базовый машинный код, который здесь не будет повторяться.

2. Что делает nodejs с uv и v8 сам по себе?

Поскольку это для разработчиков Javascript, мы не можем писать код C++/C напрямую, поэтому нам определенно нужно что-то, чтобы инкапсулировать эти коды C++/C и предоставить разработчикам набор элегантных интерфейсов, поэтому Nodejs делает это. Да. В двух словах:

Nodejs封装了所有与底层交流的信息,给开发者提供一致的接口定义。在不断升级v8和libuv的同时,依然能够做到接口的一致性,这个就是nodejs想要实现的目标。

Итак, вопрос в том, как nodejs инкапсулирует libuv и v8 и предоставляет интерфейсы? Прежде чем понять все это, давайте взглянем на структуру каталогов Nodejs, Эта структура каталогов полезна в следующих объяснениях:

Исходный код nodejs имеет два важных каталога:

  1. lib: Содержит javascript-реализации всех функций и модулей nodejs, на которые можно напрямую ссылаться в вашем js-проекте.

  2. src: Содержит реализацию всех функций в версии C++, код здесь фактически относится к Libuv и V8.

Затем мы можем посмотреть файл в каталоге lib и увидеть, что помимо обычного синтаксиса js есть метод, которого нет в обычном приложении:internalBinding. Что это? Что оно делает?

Наше путешествие начинается с этого метода и шаг за шагом углубляется в nodejs, помогая вам шаг за шагом раскрыть тайну nodejs. Прежде всего, мы должны начать с процесса компиляции nodejs.

Прежде чем говорить о процессе компиляции, мы должны популяризировать две концепции классификации модулей и связывателя загрузки C++ внутри исходного кода Nodejs.

2.1, Классификация модулей Nodejs

Модули Nodejs можно разделить на следующие три категории:

  • Базовый модуль (собственный модуль): содержится в исходном коде Node.js и скомпилирован в исполняемый двоичный файл Node.js. Модуль JavaScript, который на самом деле является файлом js в каталогах lib и deps, например, обычно используемыйhttp,fsтак далее
  • Встроенный модуль (встроенный модуль): Как правило, мы не вызываем его напрямую, а вызываем в нативном модуле, а затем требуем
  • Сторонние модули: Модули, которые поставляются с исходным кодом, отличным от Node.js, могут в совокупности называться сторонними модулями, такими как экспресс, веб-пакет и т. д.
    • Модули JavaScript, это самые распространенные, обычно мы пишем модули JavaScript при разработке
    • Модуль JSON, это очень просто, просто файл JSON
    • Модуль расширения C/C++, написанный на C/C++, с суффиксом .node после компиляции.

Напримерlibв каталогеfs.jsявляется родным модулем, иfs.jsназываетсяsrcв каталогеnode_fs.ccЭто встроенный модуль. Зная классификацию модулей, то задаетесь вопросом, как загружаются эти модули? (В этой статье не объясняется загрузка модулей, поэтому сторонние модули не обсуждаются)

2.2, Классификация связывателя загрузки C++

Следующий текст будет относиться к этим понятиям:

  • process.binding(): предыдущий загрузчик привязки C++, поскольку он является объектом, смонтированным на глобальном объекте процесса, доступен из пользовательского пространства. Эти привязки C++ используют этот макрос:NODE_BUILTIN_MODULE_CONTEXT_AWARE()для создания, и все их nm_flags установлены наNM_F_BUILTIN
  • process._linkedBinding(): для разработчиков, которые хотят добавить в свои приложения дополнительные привязки C++, используйтеNODE_MODULE_CONTEXT_AWARE_CPP()макрос для создания, его флаг установлен вNM_F_LINKED
  • internalBinding: частный внутренний загрузчик привязки C++, недоступный из пользовательского пространства, поскольку он доступен только в NativeModule.require(). использоватьNODE_MODULE_CONTEXT_AWARE_INTERNAL()макрос для создания, его флаг установлен вNM_F_INTERNAL

3. Процесс компиляции nodejs

согласно сРекомендации с официального сайта, компиляция исходников проста и груба:

$ ./configure
$ make -j4

Мы можем извлечь некоторую важную информацию из файла конфигурации сборки nodejs.

Как мы все знаем, Nodejs использует метод компиляции GYP, и его файл компиляции GYP выглядит следующим образом:node.gyp, мы получаем две важные части информации из двух мест в этом файле.

3.1. node.gyp

3.1.1. Входной файл исполняемого приложения

из этого файлаtargetИз поля видно, что после компиляции будет сгенерировано несколько целей, но самая важная — первая.target, его конфигурация:

{
  // 定义的'node_core_target_name%'就是'node',
  'target_name': '<(node_core_target_name)',
  'type': 'executable', // 这里的类型是可执行文件

  'defines': [
    'NODE_WANT_INTERNALS=1',
  ],

  'includes': [
    'node.gypi'
  ],

  'include_dirs': [
    'src',
    'deps/v8/include'
  ],

  'sources': [
    'src/node_main.cc'
  ],
  ... ...
}

Из этого видно, что файл входа всего приложения узла на самом делеnode_main.cc.

3.1.2, режим компиляции всех файлов JS в исходном коде NodeJS

Второй целью скомпилированного файла являетсяlibnode, он компилирует остальные оставшиеся файлы C++ в библиотечные файлы, но есть особое место, где цель выполняет действие перед компиляцией:

{
  // 这里定义的'node_lib_target_name'就是libnode
  'target_name': '<(node_lib_target_name)',
  'type': '<(node_intermediate_lib_type)',
  'includes': [
    'node.gypi',
  ],

  'include_dirs': [
    'src',
    '<(SHARED_INTERMEDIATE_DIR)' # for node_natives.h
  ],
  ... ...
  'actions': [
    {
      'action_name': 'node_js2c',
      'process_outputs_as_sources': 1,
      'inputs': [
        # Put the code first so it's a dependency and can be used for invocation.
        'tools/js2c.py',
        '<@(library_files)',
        'config.gypi',
        'tools/js2c_macros/check_macros.py'
      ],
      'outputs': [
        '<(SHARED_INTERMEDIATE_DIR)/node_javascript.cc',
      ],
      'conditions': [
        [ 'node_use_dtrace=="false" and node_use_etw=="false"', {
          'inputs': [ 'tools/js2c_macros/notrace_macros.py' ]
        }],
        [ 'node_debug_lib=="false"', {
          'inputs': [ 'tools/js2c_macros/nodcheck_macros.py' ]
        }],
        [ 'node_debug_lib=="true"', {
          'inputs': [ 'tools/js2c_macros/dcheck_macros.py' ]
        }]
      ],
      'action': [
        'python', '<@(_inputs)',
        '--target', '<@(_outputs)',
      ],
    },
  ],

Из этой информации о конфигурации говорится, что существуетjs2c.pyФайл Python будетlib/**/*.jsа такжеdeps/**/*.jsВсе файлы js преобразуются в массивы в соответствии с их кодами ASCII и помещаются вnode_javascript.ccв файле.

Сгенерированоnode_javascript.ccСодержимое файла примерно такое:

namespace node {

namespace native_module {
  ...

  static const uint8_t fs_raw[] = {...}

  ...

  void NativeModuleLoader::LoadJavaScriptSource() {
    ...
    source_.emplace("fs", UnionBytes{fs_raw, 50659});
    ...
  }
  UnionBytes NativeModuleLoader::GetConfig() {
    return UnionBytes(config_raw, 3017);  // config.gypi
  }
}

Этот подход напрямую кэширует все файлы js в памяти, избегая избыточных операций ввода-вывода и повышая эффективность.

Следовательно, из приведенной выше информации о конфигурации мы можем подытожить такой процесс компиляции:

Что ж, после того, как процесс компиляции ясен, давайте проанализируем его с момента запуска nodejs.internalBindingКак это свято.

4. Процесс запуска nodejs

В предыдущем разделе мы знаем, что входной файл приложения nodejsnode_main.cc, поэтому мы начинаем отслеживать код из этого файла и получаем следующую блок-схему:

Те, что отмечены красным, являются ключевыми моментами, на которые необходимо обратить внимание, некоторые знания в ней могут быть связаны с предыдущими статьями.Потребовалось два месяца, самая полная оригинальная серия статей Nodejs в Интернете (статьи до 100 000 слов, добро пожаловать в коллекцию)Некоторые из основных статей, перечисленных в статье, я считаю, что у меня возникает чувство внезапного осознания, когда я вижу это здесь, я чувствую, что точки знаний могут быть связаны одновременно, в этом прелесть систематического обучения ~

Возвращаясь к картинке выше, все подсказки сосредоточены на этой функции:NativeModuleLoader::LookupAndCompile. Перед вызовом этой функции есть еще один важный момент:В этот момент создается экземпляр NativeModuleLoader, поэтому его конструктор выполняется, а его конструктор выполняет только одну функцию: LoadJavaScriptSource(), которую мы видели в файле node_javascript.cc в предыдущем разделе., то имеем следующие выводы:

  • internal/bootstrap/loader.jsэто первый файл js, который мы выполняем

ТакNativeModuleLoader::LookupAndCompileЧто вы наделали?

4.1,NativeModuleLoader::LookupAndCompile

Он использует наш входящий идентификатор файла (эта передачаinternal/bootstrap/loader.js)существует_sourceНайти в переменной, обернуть все содержимое файла в новую функцию после его нахождения и добавить некоторые определения функций (на этот раз переданныйgetLinkedBindingа такжеgetInternalBinding), чтобы в файле js можно было вызвать эти функции C++, а затем выполнить новую функцию. Передача этого параметра показана на рисунке вышеEnvironment::BootstrapInternalLoadersВ функции:

MaybeLocal<Value> Environment::BootstrapInternalLoaders() {
  EscapableHandleScope scope(isolate_);

  // Create binding loaders
  std::vector<Local<String>> loaders_params = {
      process_string(),
      FIXED_ONE_BYTE_STRING(isolate_, "getLinkedBinding"),
      FIXED_ONE_BYTE_STRING(isolate_, "getInternalBinding"),
      primordials_string()};
  // 这里的GetInternalBinding便是我们调用`getInternalBinding`执行的函数。如果你不知道为什么js可以调用C++函数的话,请参考这篇文章:《如何正确地使用v8嵌入到我们的C++应用中》
  std::vector<Local<Value>> loaders_args = {
      process_object(),
      NewFunctionTemplate(binding::GetLinkedBinding)
          ->GetFunction(context())
          .ToLocalChecked(),
      NewFunctionTemplate(binding::GetInternalBinding)
          ->GetFunction(context())
          .ToLocalChecked(),
      primordials()};
      ...
}

Загрузить на этот разloader.jsПосле этого посмотрим, что делает этот файл?

4.2,internal/bootstrap/loader.js

Этот файл очень особенный. Это единственный файл js без ключевого слова require. Единственные внешние функции, которые он использует, — это только что упомянутые getLinkedBinding и getInternalBinding. Это можно проверить по исходному коду файла.

Этот файл для сборкиNativeModuleТакой объект имеет несколько методов-прототипов и, наконец, возвращает такую ​​структуру данных:

const loaderExports = {
  internalBinding,
  NativeModule,
  require: nativeModuleRequire
};

Внутри мы нашлиinternalBindingОригинальная реализация этого метода:

let internalBinding;
{
  const bindingObj = Object.create(null);
  // eslint-disable-next-line no-global-assign
  internalBinding = function internalBinding(module) {
    let mod = bindingObj[module];
    if (typeof mod !== 'object') {
      // 这里调用我们的C++方法
      mod = bindingObj[module] = getInternalBinding(module);
      moduleLoadList.push(`Internal Binding ${module}`);
    }
    return mod;
  };
}

Затем мы следуем по виноградной лозе и смотрим на красную линию на блок-схеме выше,loader.jsВозвращаемое значение после выполнения продолжает передаваться вinternal/bootstrap/node.jsЭтот файл использует .

код показывает, как показано ниже:

MaybeLocal<Value> Environment::BootstrapInternalLoaders() {
  ... ...
  // 这里的loader_exports便是执行完loader.js之后返回的值
  Local<Value> loader_exports;
  if (!ExecuteBootstrapper(
           this, "internal/bootstrap/loaders", &loaders_params, &loaders_args)
           .ToLocal(&loader_exports)) {
    return MaybeLocal<Value>();
  }
  CHECK(loader_exports->IsObject());
  Local<Object> loader_exports_obj = loader_exports.As<Object>();

  // 此时internal_binding_loader的值便是loader_exports.internalBinding,下面的同理
  Local<Value> internal_binding_loader =
      loader_exports_obj->Get(context(), internal_binding_string())
          .ToLocalChecked();
  CHECK(internal_binding_loader->IsFunction());
  set_internal_binding_loader(internal_binding_loader.As<Function>());

  // 注意这里的require是native_module的require,有别于第三方包的reuqire
  Local<Value> require =
      loader_exports_obj->Get(context(), require_string()).ToLocalChecked();
  CHECK(require->IsFunction());
  set_native_module_require(require.As<Function>());
  ...
}

MaybeLocal<Value> Environment::BootstrapNode() {
  ... ...
  std::vector<Local<Value>> node_args = {
      process_object(),
      native_module_require(),
      internal_binding_loader(), // 这个就是刚才的那个internalBinding
      Boolean::New(isolate_, is_main_thread()),
      Boolean::New(isolate_, owns_process_state()),
      primordials()};
  ... ...
}

Аналогично, файл также будет инжектированisMainThread,ownsProcessStateтак же какprocess,require,primordialsа такжеinternalBindingШесть функций C++ вызываются файлами js.

Из этого следует еще один вывод:

  • js вызывает internalBinding => функцию internal_binding_loader в C++ => функцию internalBinding в js => функцию GetInternalBinding в C++

Но здесь у нас все еще есть некоторые проблемы, которые не решены, и нам нужно продолжать углубляться.

4.3,GetInternalBinding

существуетinternal/bootstrap/node.jsбольшинстваprocessа такжеglobalИнициализация присваивания объекта, согласно приведенному выше выводу, при вызовеinternalBinding, то, что на самом деле будет выполнено,GetInternalBindingэта функция С++. Итак, давайте посмотрим на реализацию этой функции.

Правила для js, вызывающие функции C++, находятся вКак правильно использовать v8 для встраивания в наше приложение на C++Об этом упоминалось в статье, поэтому не будем повторяться, как это называется, сосредоточимся на:

void GetInternalBinding(const FunctionCallbackInfo<Value>& args) {
  ... ...
  // 查找模块,在哪里查找?
  node_module* mod = FindModule(modlist_internal, *module_v, NM_F_INTERNAL);
  if (mod != nullptr) {
    exports = InitModule(env, mod, module);
    // 什么是constants模块?
  } else if (!strcmp(*module_v, "constants")) {
    exports = Object::New(env->isolate());
    CHECK(
        exports->SetPrototype(env->context(), Null(env->isolate())).FromJust());
    DefineConstants(env->isolate(), exports);
  } else if (!strcmp(*module_v, "natives")) {
    exports = native_module::NativeModuleEnv::GetSourceObject(env->context());
    // Legacy feature: process.binding('natives').config contains stringified
    // config.gypi
    CHECK(exports
              ->Set(env->context(),
                    env->config_string(),
                    native_module::NativeModuleEnv::GetConfigString(
                        env->isolate()))
              .FromJust());
  } else {
    return ThrowIfNoSuchModule(env, *module_v);
  }

  // 这里导出了exports这个变量~
  args.GetReturnValue().Set(exports);
}

Эта функция оставляет нам несколько вопросов:

  • Откуда берется modlist_internal в FindModule?
  • Почему родное имя модуля все еще называетсяconstantsа такжеnativesчто о?

Чтобы разгадать эти вопросы, мы продолжаем копать глубже.

4.4,NODE_MODULE_CONTEXT_AWARE_INTERNAL

В настоящее времяNODE_MODULE_CONTEXT_AWARE_INTERNALГрандиозный дебют, заботливая детская обувь должна найти такие, какsrc/node_fs.ccВсе такие файлы заканчиваются этим определением макроса.

существуетnode_binding.hЕго определение можно найти в файле:

#define NODE_MODULE_CONTEXT_AWARE_INTERNAL(modname, regfunc)                   \
  NODE_MODULE_CONTEXT_AWARE_CPP(modname, regfunc, nullptr, NM_F_INTERNAL

Вы можете видеть, что фактический вызов является определением макросаNODE_MODULE_CONTEXT_AWARE_CPP, Просто установите ФЛАГ наNM_F_INTERNAL.

а такжеNODE_MODULE_CONTEXT_AWARE_CPPОпределение макроса фактически вызывает метод:node_module_register.

node_module_registerЭтот метод должен перейти к глобальной статической переменнойmodlist_internalа такжеmodlist_linkedДва списка модули модулей:

if (mp->nm_flags & NM_F_INTERNAL) {
    mp->nm_link = modlist_internal;
    modlist_internal = mp;
} else if (!node_is_initialized) {
  // "Linked" modules are included as part of the node project.
  // Like builtins they are registered *before* node::Init runs.
  mp->nm_flags = NM_F_LINKED;
  mp->nm_link = modlist_linked;
  modlist_linked = mp;
} else {
  thread_local_modpending = mp;
}

Таким образом, modlist_internal — это связанный список, который связывает все встроенные модули, поэтому приведенное вышеGetInternalBindingМетод представляет собой логику выполнения, подобную этой:

те что на картинке вышеinternalBindingВызов предоставляет множество имен модулей, среди которых мы только что спросилиconstantsа такжеnativesЭти два специальных имени модуля.

Таким образом, две проблемы выше решены.

Но проблема действительно решена? Если вы просто просто компилируйте файл, этоNODE_MODULE_CONTEXT_AWARE_INTERNALзвонить не будут, так откуда звонок?node_module_register?

🙆, я ценю ваш стойкий дух. Последний вопрос вместе с кратким изложением всей статьи предоставляется всем, что является большим пасхальным яйцом~

4.5, идеальная большая картина

На приведенном выше рисунке показана полная блок-схема работы nodejs с libuv и v8.Там есть момент, чтобы объяснить проблему только сейчас: когда загружать все встроенные модули вmodlist_internalиз? Ответ заключается в вызове при запуске nodejsbinding::RegisterBuiltinModules().

На этом логично, что всю статью можно закончить, но чтобы закрепить наше предыдущее обучение (чжуан) обучение (би), мы все же решили взять пример, чтобы посмотреть на предыдущуюКак правильно использовать v8 для встраивания в наше приложение на C++Так много теорий, упомянутых в статье, все ли они верны в исходном коде Nodejs?

5. Дайте 🌰 (пасхальное яйцо~)

Допустим есть такой index.js:

const fs = require('fs')

module.exports = () => {
  fs.open('test.js', () => {
    // balabala
  })
}

при вводе в командной строкеnode index.jsКаков будет процесс обработки после возврата каретки?

Этот вопрос действительно слишком TMD, например: «Когда вы вводите URL-адрес в браузере и нажимаете Enter, через какой процесс он пройдет». К счастью, это не интервью (вероятно, это будет вопрос интервью~)

Это всего лишь две или три строчки кода с первого взгляда? Но с такими простыми двумя-тремя строчками кода можно задать много вопросов на собеседовании~ Например:

  • почему здесьrequireМожно ли цитировать напрямую без декларации?
  • Можно ли здесь заменить module.export на export?
  • Есть ли в fs.open синхронизированный метод?
  • fs.open может передавать значение для указания режима открытия Что означает это "0o666"?
  • Нижний слой fs.open вызывает uv_fs_open, он выполняется в основном потоке libuv или в другом потоке?

Есть еще много вопросов, которые нужно задать, поэтому я не буду перечислять их здесь. Если вы хотите больше вопросов, пожалуйста, оставьте сообщение (😏)

Сегодня мы сосредоточимся не на этих вопросах интервью, а на проверке того, соответствует ли код C++ тому, что написано в предыдущей статье. Разбираем прошлое построчно (не углубляясь).

5.1,require('fs')

когда тыrequireкогда на самом деле nodejs не выполняет напрямую какой-либо код, который вы пишете в файле js (за исключением вышеупомянутогоinternal/bootstrap/loader.jsа такжеinternal/bootstrap/node.js). Он помещает ваш код в функцию-оболочку, а затем выполняет функцию-оболочку. Вот почему переменные верхнего уровня, определенные в любом модуле, остаются в рамках этого модуля.

Например:

~ $ node
> require('module').wrapper
[ '(function (exports, require, module, __filename, __dirname) { ',
  '\n});' ]
>

Вы можете видеть, что функция-обертка имеет 5 параметров:exports, require, module, __filenameа также__dirname, Таким образом, require и module.exports, которые вы пишете в файле js, на самом деле являются этими параметрами, а не совсемглобальная переменная

Более подробная информация не будет расширена или не будет завершена ~

5.2,fs.open

На открытый файл js не обращает внимания и наконец вызывает:

binding.open(pathModule.toNamespacedPath(path),
               flagsNumber,
               mode,
               req);

Затем мы прыгаем вnode_fs.cc, шаг за шагом, чтобы проверить предыдущую теорию.

5.2.1,Initialize

Помните, что в ультимативном яйце на картинке выше при вызовеinternalBindingКогда соответствующий встроенный модуль инициализируется, то есть вызывается его функция инициализации, вотInitializeфункция.

Эта функция изначально даетtargetУстановите метод, например:

env->SetMethod(target, "close", Close);
env->SetMethod(target, "open", Open);

Затем, наконец, вызывается методthat->Set(context, name_string, function).Check();, это у нас?Как правильно использовать v8 для встраивания в наше приложение на C++второй раздел2, вызов функций C ++Сказать точно так же?

Затем начните выставлятьFSReqCallbackэтот класс, это вfs.jsВ файле есть вызов:

const req = new FSReqCallback();
req.oncomplete = callback;

Затем мы будем использоватьКак правильно использовать v8 для встраивания в наше приложение на C++третий раздел3. Используйте классы C++Знание:

Local<FunctionTemplate> fst = env->NewFunctionTemplate(NewFSReqCallback);
fst->InstanceTemplate()->SetInternalFieldCount(1);
fst->Inherit(AsyncWrap::GetConstructorTemplate(env));
Local<String> wrapString =
    FIXED_ONE_BYTE_STRING(isolate, "FSReqCallback");
fst->SetClassName(wrapString);
target
    ->Set(context, wrapString,
          fst->GetFunction(env->context()).ToLocalChecked())
    .Check();

Это прекрасно согласуется с теоретическими знаниями, обсуждавшимися ранее.

Тогда давайте посмотрим, как использовать libuv

5.2.2,Open

Асинхронные вызовы единообразно инкапсулируютAsyncCallфункция, которая снова вызываетAsyncDestCall:

AsyncCall(env, req_wrap_async, args, "open", UTF8, AfterInteger,
              uv_fs_open, *path, flags, mode);

Последующие вызовы остаются такими же, как и раньше вfs.cПриведенный пример просто для инкапсуляции, и многое скрыто, и его труднее читать.

На этом 💐Вы закончили читать эту статью, и спасибо за ваше терпение, чтобы позволить вам освоить еще одну часть знаний.Если вы еще не читали ее, нажмите на коллекцию, и вы можете взять ее для справки, когда ты столкнешься с этим в будущем~

Спасибо~

Ссылаться на

  1. Internals of Node- Advance node
  2. В сочетании с анализом исходного кода загрузка модуля Node.js и принцип работы