博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Node.js源码解析-启动-js部分
阅读量:6171 次
发布时间:2019-06-21

本文共 6688 字,大约阅读时间需要 22 分钟。

Node.js源码解析-启动-js部分

欢迎来我的博客阅读:

Node.js 版本 8.x

Node.js 进程启动时,首先执行 c / c++ 代码,然后 c / c++ 加载并执行 lib/internal/bootstrap_node.js 并给予一个 process 参数( 运行上下文 )

// lib/internal/bootstrap_node.js 概览// Hello, and welcome to hacking node.js!//// This file is invoked by node::LoadEnvironment in src/node.cc, and is// responsible for bootstrapping the node.js core. As special caution is given// to the performance of the startup process, many dependencies are invoked// lazily.'use strict';// 这里的 process 对象来自 c / c++,属于原始数据(function(process) {  // ...  startup();})

加载 lib/internal/bootstrap_node.js 后,直接执行 startup() 函数

startup()

// lib/internal/bootstrap_node.js  function startup() {    // 下面几行代码使 process 具有 EventEmitter 的特性,比如说 on,emit    // BEGIN     const EventEmitter = NativeModule.require('events');    process._eventsCount = 0;    const origProcProto = Object.getPrototypeOf(process);    Object.setPrototypeOf(process, Object.create(EventEmitter.prototype, {      constructor: Object.getOwnPropertyDescriptor(origProcProto, 'constructor')    }));    // 相当于 new EventEmitter() ,不过上下文是 process    EventEmitter.call(process);    // END    // 一些初始化操作    // BEGIN    setupProcessObject();    // do this good and early, since it handles errors.    setupProcessFatal();    setupProcessICUVersions();    setupGlobalVariables();    if (!process._noBrowserGlobals) {      setupGlobalTimeouts();      setupGlobalConsole();    }    // END    // process 对象的初始化操作    // BEGIN    // 这里的 internal/process 模块用于初始化 process     const _process = NativeModule.require('internal/process');    _process.setup_hrtime();    _process.setup_cpuUsage();    _process.setupMemoryUsage();    _process.setupConfig(NativeModule._source);    NativeModule.require('internal/process/warning').setup();    NativeModule.require('internal/process/next_tick').setup();    NativeModule.require('internal/process/stdio').setup();    _process.setupKillAndExit();    _process.setupSignalHandlers();    if (global.__coverage__)      NativeModule.require('internal/process/write-coverage').setup();    if (process.argv[1] !== '--debug-agent')      _process.setupChannel();    _process.setupRawDebug();        NativeModule.require('internal/url');    Object.defineProperty(process, 'argv0', {      enumerable: true,      configurable: false,      value: process.argv[0]    });    process.argv[0] = process.execPath;    // ...    // END    // 下面的 if-else 用来判断 node 的运行模式,我们只关注 node xx.js 的运行模式    // if () {    // ...    } else {      // 执行用户代码      // cluster 模块的 hook      if (process.argv[1] && process.env.NODE_UNIQUE_ID) {        const cluster = NativeModule.require('cluster');        cluster._setupWorker();        // Make sure it's not accidentally inherited by child processes.        delete process.env.NODE_UNIQUE_ID;      }      if (process._eval != null && !process._forceRepl) {        // ...      } else if (process.argv[1] && process.argv[1] !== '-') {        // node app.js        // make process.argv[1] into a full path        const path = NativeModule.require('path');        // 变为绝对路径        process.argv[1] = path.resolve(process.argv[1]);        const Module = NativeModule.require('module');        // check if user passed `-c` or `--check` arguments to Node.        if (process._syntax_check_only != null) {          const fs = NativeModule.require('fs');          // read the source          const filename = Module._resolveFilename(process.argv[1]);          var source = fs.readFileSync(filename, 'utf-8');          checkScriptSyntax(source, filename);          process.exit(0);        }        preloadModules();        Module.runMain();      } else {        // REPL 或其他      }    }  }

startup() 最后调用 Module.runMain() 函数来加载并执行用户代码。在执行 startup() 函数的过程中,多次用到了 NativeModule.require() 来加载模块

NativeModule

NativeModule.require() 是专门用来加载 Node.js 内置模块的

// lib/internal/bootstrap_node.js  function NativeModule(id) {    this.filename = `${id}.js`;    this.id = id;    this.exports = {};    this.loaded = false;    this.loading = false;  }  NativeModule._source = process.binding('natives');  NativeModule._cache = {};  NativeModule.require = function(id) {    if (id === 'native_module') {      return NativeModule;    }    const cached = NativeModule.getCached(id);    if (cached && (cached.loaded || cached.loading)) {      return cached.exports;    }    if (!NativeModule.exists(id)) {      // Model the error off the internal/errors.js model, but      // do not use that module given that it could actually be      // the one causing the error if there's a bug in Node.js      const err = new Error(`No such built-in module: ${id}`);      err.code = 'ERR_UNKNOWN_BUILTIN_MODULE';      err.name = 'Error [ERR_UNKNOWN_BUILTIN_MODULE]';      throw err;    }    process.moduleLoadList.push(`NativeModule ${id}`);    const nativeModule = new NativeModule(id);    nativeModule.cache();    nativeModule.compile();    return nativeModule.exports;  };  NativeModule.getCached = function(id) {    return NativeModule._cache[id];  };  NativeModule.exists = function(id) {    return NativeModule._source.hasOwnProperty(id);  };  // ...  NativeModule.wrap = function(script) {    return NativeModule.wrapper[0] + script + NativeModule.wrapper[1];  };  NativeModule.wrapper = [    '(function (exports, require, module, __filename, __dirname) { ',    '\n});'  ];  NativeModule.prototype.compile = function() {    var source = NativeModule.getSource(this.id);    source = NativeModule.wrap(source);    this.loading = true;    try {      const fn = runInThisContext(source, {        filename: this.filename,        lineOffset: 0,        displayErrors: true      });      fn(this.exports, NativeModule.require, this, this.filename);      this.loaded = true;    } finally {      this.loading = false;    }  };  NativeModule.prototype.cache = function() {    NativeModule._cache[this.id] = this;  };

NativeModule 有几个重要的属性和方法:

  • id: NativeModule 的标识符,例如 eventsinternal/process

  • filename: NativeModule 对应源码文件

  • exports: 默认值是 {}

  • loaded / loading: NativeModule 状态

  • _cache: 简单的模块缓存

  • _source: 模块源码资源

  • require(): 先查询缓存,缓存没有则新建 NativeModule 并编译,返回 exports

  • wrap()/wrapper: wrapper 是对模块上下文的包裹,如下:

    (function (exports, require, module, __filename, __dirname) {   xxx});
  • compile(): 将模块源码用 wrapper 包裹后,使用 runInThisContext()(类似 eval())生成 js 函数,再执行之

Module.runMain()

Node.js 启动完成后,调用 Module.runMain(),源码如下:

// bootstrap main module.Module.runMain = function() {  // Load the main module--the command line argument.  Module._load(process.argv[1], null, true);  // Handle any nextTicks added in the first tick of the program  process._tickCallback();};

Module._load() 加载并执行用户代码。至此 启动-js部分 已经全部完成,后续模块加载部分,见

End

启动只是 Node.js 源码的一小部分,除此之外还有大量的内置模块和 c / c++ 源码

参考:

转载地址:http://getba.baihongyu.com/

你可能感兴趣的文章
<s:iterator>标签迭代数据不显示
查看>>
判断 SQLServer 触发器类型,支持多行
查看>>
SQL表连接查询(inner join、full join、left join、right join)
查看>>
阿里云OTS(开放结构化数据服务)可视化管理工具的设计和功能介绍
查看>>
Github创建分支
查看>>
转换PHP脚本成为windows的执行程序
查看>>
Python组织文件 实践:将带有美国风格日期的文件改名为欧洲风格日期
查看>>
FATAL ERROR: Could not find ./bin/my_print_defaults 解决方法
查看>>
Java实验四和实验五
查看>>
树讲解(7)——没有上司的舞会
查看>>
学习:jar 的创建,执行,引用;在 cmd 下实践
查看>>
模板方法模式-Template Method
查看>>
java格式化输出方法
查看>>
在windows下搭建网站开发环境apache+php+mysql模式
查看>>
软件开发流程思考及建议
查看>>
liunx基础入门了解
查看>>
Core BlueTooth官方文档翻译
查看>>
通过实体类及映射文件生成数据库表
查看>>
java并发编程_CountDownLanch(倒计数锁存器)应用场景
查看>>
java开发人员win10配置
查看>>