`

node.js 源码分析

 
阅读更多

node.js脚本文件是node.js的js部分的入口。它里面有几个重要的对象:

1.NativeModule:

Nodejs的模块分为两部分,一部分是用C实现的模块,比如:buffer、stdio等,另一部分是用js实现的模块比如net、dns等。对于C的模块,nodejs通过node.h提供的NODE_MODULE方法将模块存储在变量_module里。在srcde_extensions.cc中提供了get_builtin_module(name)接口获取这些模块;而对于js的模块,nodejs在srcde.js中实现了一个NativeModule对象用于管理js模块,它通过调用process.binding(“natives”)把所有内置的js模块放在NativeModule._source上,并提供require接口供调用。
为了提高模块加载的效率,nodejs在binding函数和require函数中都增加了缓存机制,在首次加载模块的时候,模块会copy的缓存中,以后的模块加载实际上只是从缓存中获得模块的接口,这样的机制使得nodejs在模块加载方面十分高效。
Nodejs模块的调用形式如下:
加载C++模块(以stdio为例):
process.binding("stdio")->get_builtin_module("stdio")-> _module -> NODE_MODULE(node_stdio, node::Stdio::Initialize)(定义)
加载js模块(以net为例)
require("net") -> NativeModule.require("net") -> process.binding("natives")["net"] -> DefineJavaScript() -> natives[] -> node_natives.h

function NativeModule(id) {
    this.filename = id + '.js';
    this.id = id;
    this.exports = {};
    this.loaded = false;
  }

  NativeModule._source = process.binding('natives');// 加载内部模块
  NativeModule._cache = {};
  // 主要就是这个方法
  NativeModule.require = function(id) {
    if (id == 'native_module') {
      return NativeModule;
    }

    var cached = NativeModule.getCached(id);
    if (cached) { 
      return cached.exports; // 如果存在缓存,直接返回
    }

    if (!NativeModule.exists(id)) {
      throw new Error('No such native module ' + id); // ID错误
    }

    process.moduleLoadList.push('NativeModule ' + id);

    var 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.getSource = function(id) {
    return NativeModule._source[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);

    var fn = runInThisContext(source, this.filename, true);
    fn(this.exports, NativeModule.require, this, this.filename);

    this.loaded = true;
  };

  NativeModule.prototype.cache = function() {
    NativeModule._cache[this.id] = this;
  };

 

 2.startup:

 

(function(process) { 
  this.global = this;
  startup();
});
 整个node.js文件就是一个函数对象的定义,而这个函数只做了两件事,所有到逻辑都在startup里面。

 

 

function startup() { 
    var EventEmitter = NativeModule.require('events').EventEmitter;

    process.__proto__ = Object.create(EventEmitter.prototype, {
      constructor: {
        value: process.constructor
      }
    });
    EventEmitter.call(process);

    process.EventEmitter = EventEmitter; // process.EventEmitter is deprecated 已经过时

    // do this good and early, since it handles errors.
    startup.processFatal(); // 异常处理模块

    startup.globalVariables(); // 全局属性初始化
    startup.globalTimeouts(); // 定义全局时间方法
    startup.globalConsole(); // 加载控制台模块

    startup.processAssert(); // 定义断言对象
    startup.processConfig(); // 加载process.config里面的配置内容,保存做process.config中
    startup.processNextTick(); // 定义process.nextTick方法,这个方法是让一个行为在下一次事件轮循的时候执行。
    startup.processStdio(); // 标准流
    startup.processKillAndExit(); // 定义进程到kill和exit方法
    startup.processSignalHandlers(); // 定义为进程添加信号的方法,这里的信号是指事件

    startup.processChannel(); // 这个暂时不清楚

    startup.resolveArgv0();

    // There are various modes that Node can run in. The most common two
    // are running from a script and running the REPL - but there are a few
    // others like the debugger or running --eval arguments. Here we decide
    // which mode we run in.

    if (NativeModule.exists('_third_party_main')) {
      // To allow people to extend Node in different ways, this hook allows
      // one to drop a file lib/_third_party_main.js into the build
      // directory which will be executed instead of Node's normal loading.
      process.nextTick(function() {
        NativeModule.require('_third_party_main');
      });

    } else if (process.argv[1] == 'debug') {
      // Start the debugger agent
      var d = NativeModule.require('_debugger');
      d.start();

    } else if (process._eval != null) {
      // User passed '-e' or '--eval' arguments to Node.
      evalScript('[eval]');
    } else if (process.argv[1]) { // 有第一个参数到时候
      // make process.argv[1] into a full path
      var path = NativeModule.require('path');
      process.argv[1] = path.resolve(process.argv[1]);

      // If this is a worker in cluster mode, start up the communiction
      // channel.
      if (process.env.NODE_UNIQUE_ID) {
        var cluster = NativeModule.require('cluster');
        cluster._setupWorker();

        // Make sure it's not accidentally inherited by child processes.
        delete process.env.NODE_UNIQUE_ID;
      }

      var Module = NativeModule.require('module');

      if (global.v8debug &&
          process.execArgv.some(function(arg) {
            return arg.match(/^--debug-brk(=[0-9]*)?$/);
          })) {

        // XXX Fix this terrible hack!
        //
        // Give the client program a few ticks to connect.
        // Otherwise, there's a race condition where `node debug foo.js`
        // will not be able to connect in time to catch the first
        // breakpoint message on line 1.
        //
        // A better fix would be to somehow get a message from the
        // global.v8debug object about a connection, and runMain when
        // that occurs.  --isaacs

        var debugTimeout = +process.env.NODE_DEBUG_TIMEOUT || 50;
        setTimeout(Module.runMain, debugTimeout);

      } else {
        // Main entry point into most programs:
        Module.runMain();
      }

    } else { // 没有参数的时候
      var Module = NativeModule.require('module');

      // If -i or --interactive were passed, or stdin is a TTY.
      if (process._forceRepl || NativeModule.require('tty').isatty(0)) {
        // REPL
        var opts = {
          useGlobal: true,
          ignoreUndefined: false
        };
        if (parseInt(process.env['NODE_NO_READLINE'], 10)) {
          opts.terminal = false;
        }
        if (parseInt(process.env['NODE_DISABLE_COLORS'], 10)) {
          opts.useColors = false;
        }
        var repl = Module.requireRepl().start(opts);
        repl.on('exit', function() {
          process.exit();
        });

      } else {
        // Read all of stdin - execute it.
        process.stdin.setEncoding('utf8');

        var code = '';
        process.stdin.on('data', function(d) {
          code += d;
        });

        process.stdin.on('end', function() {
          process._eval = code;
          evalScript('[stdin]');
        });
      }
    }

  startup.globalVariables = function() { // 这个方法很蛋疼啊,定义这么多重复变量吃啊
    global.process = process;
    global.global = global;
    global.GLOBAL = global;
    global.root = global;
    global.Buffer = NativeModule.require('buffer').Buffer;
    process.binding('buffer').setFastBufferConstructor(global.Buffer);
    process.domain = null;
    process._exiting = false;
  };

startup.globalTimeouts = function() {
    global.setTimeout = function() {
      var t = NativeModule.require('timers');
      return t.setTimeout.apply(this, arguments);
    };

    global.setInterval = function() {
      var t = NativeModule.require('timers');
      return t.setInterval.apply(this, arguments);
    };

    global.clearTimeout = function() {
      var t = NativeModule.require('timers');
      return t.clearTimeout.apply(this, arguments);
    };

    global.clearInterval = function() {
      var t = NativeModule.require('timers');
      return t.clearInterval.apply(this, arguments);
    };

    global.setImmediate = function() {
      var t = NativeModule.require('timers');
      return t.setImmediate.apply(this, arguments);
    };

    global.clearImmediate = function() {
      var t = NativeModule.require('timers');
      return t.clearImmediate.apply(this, arguments);
    };
  };
  }

 

 执行脚本部分代码:

function evalScript(name) {
    var Module = NativeModule.require('module');
    var path = NativeModule.require('path');
    var cwd = process.cwd();

    var module = new Module(name);
    module.filename = path.join(cwd, name);
    module.paths = Module._nodeModulePaths(cwd);
    var script = process._eval;
    if (!Module._contextLoad) {
      var body = script;
      script = 'global.__filename = ' + JSON.stringify(name) + ';\n' +
               'global.exports = exports;\n' +
               'global.module = module;\n' +
               'global.__dirname = __dirname;\n' +
               'global.require = require;\n' +
               'return require("vm").runInThisContext(' +
               JSON.stringify(body) + ', ' +
               JSON.stringify(name) + ', true);\n';
    }
    var result = module._compile(script, name + '-wrapper');
    if (process._print_eval) console.log(result);
  }

 nodejs执行脚本的方式有两种,一种是process进程对象的._compile方法,上面已经给出了。另外一种是evals模块的Script对象。module._compile(script, name + '-wrapper');。Script = process.binding('evals').Script,Script.runInThisContext(),Script.runInNewContext()。他们的本质是一样的,都是把内容传给V8解析执行。

var NativeModule = require('native_module');
var Script = process.binding('evals').NodeScript;
var runInThisContext = Script.runInThisContext;
var runInNewContext = Script.runInNewContext;

 

分享到:
评论

相关推荐

    Node.js-深入理解Node.js核心思想与源码分析

    《Node.js-深入理解Node.js核心思想与源码分析》 Node.js,作为一个基于Chrome V8引擎的JavaScript运行环境,自其诞生以来,就以其高效、异步非阻塞I/O以及事件驱动的特性,迅速在Web开发领域崭露头角。本资料将带...

    移动端饿了么+node.js源码server.zip

    5. **Node.js源码分析**: - **路由(Routing)**: Node.js服务器通过定义不同的路由来处理不同URL的请求,例如获取商品列表、创建订单等。开发者可以学习如何在Node.js中设置和处理路由。 - **中间件(Middleware...

    node.js 源码 *nix版

    在分析“node.js 源码 *nix版”时,我们将关注与 *nix(Unix-like)操作系统相关的源码编译过程,以及遇到的问题和解决方案。 标题中提到的“*nix版”意味着我们讨论的是 Node.js 在类 Unix 系统(如 Linux 或 ...

    深入理解Node.js:核心思想与源码分析

    例如,《深入理解Node.js:核心思想与源码分析》这本书就基于node v6.0.0版本进行源码分析,书中详细阐述了Node.js异步IO和事件循环的核心思想,以及如何通过分析核心模块的实现来帮助开发者更好地使用Node.js。...

    Node.js-NodeJS源码分析-由浅入深解析架构以及运行原理

    总的来说,Node.js的源码分析涵盖了事件循环机制、V8引擎的使用、模块系统的设计、异步I/O的实现等多个方面。深入学习这些内容,不仅可以帮助开发者更好地利用Node.js,还能为他们提供宝贵的系统级编程经验。通过...

    node.js 源码

    总之,深入学习Node.js的源码,不仅可以帮助你更好地理解和利用这个强大的工具,还能提升你的系统编程和JavaScript编程能力,为你的职业发展打下坚实基础。记得在阅读过程中结合实际项目实践,理论与实践相结合,...

    Node.js-《Node.js调试指南》

    总之,《Node.js调试指南》涵盖了Node.js开发中的各种调试技术,从基础到高级,从同步到异步,从错误处理到性能分析,全面地指导开发者提升调试技能,从而编写更稳定、更高效的Node.js应用。通过学习和实践这些技巧...

    node.js博客案例详细源码 干货干货干货

    这个"node.js博客案例详细源码 干货干货干货"项目,显然是一份使用Node.js开发个人博客系统的完整源代码,对于学习Node.js、Web开发以及数据库操作有着极高的实践价值。 首先,我们要理解Node.js的核心特性。Node....

    vue.js+node.js 实战项目视频及源码

    在本资源中,你将学习如何结合 Vue.js 和 Node.js 进行实战项目的开发...通过这个实战项目,你将全面了解Vue.js和Node.js的结合使用,从需求分析、设计、编码到测试和部署,掌握完整的Web开发流程,提升实际开发能力。

    Professional Node.js 高级编程 (英文pdf+源码)

    11. **源码分析**:书中的源码部分可以帮助读者深入理解Node.js的内部工作原理,以及实际项目中的代码组织和设计模式。 12. **实时应用**:Node.js常用于构建实时应用,如聊天室、协作编辑工具,书中可能会有相关的...

    Node.js入门经典源代码

    Node.js是一种基于Chrome V8引擎的JavaScript运行环境,它允许开发者在服务器端使用JavaScript进行编程。这个"Node.js入门经典源代码"压缩包文件显然包含了用于学习和理解Node.js基础知识和实践的源代码示例。以下是...

    node-v9.11.2-win-x64-安装文件.zip

    Node.js 主要应用于构建 Web 服务器、实时通信应用(如聊天应用)、命令行工具、API 服务、构建工具(如 Webpack、Gulp)、数据处理和分析等场景。 7. **Node.js 与前端开发** 虽然 Node.js 主要用于后端开发,但...

    深入理解Node.js:核心思想与源码分析 epub格式电子书

    深入理解Node.js:核心思想与源码分析 epub 电子书格式的

    nodejs实现远程监测系统,node性能监控,Node.js源码.zip

    在本文中,我们将深入探讨如何使用Node.js构建一个远程监测系统,重点关注Node.js的性能监控。Node.js以其异步、事件驱动的特性在构建高性能...源码分析将帮助你理解实际的实现细节,进一步提升你的Node.js开发能力。

    Node.js v7.0.0源码

    源码分析可以帮助我们深入了解Node.js的工作原理,提升对JavaScript运行机制的理解。 一、V8引擎升级 在Node.js v7.0.0中,使用的V8引擎也相应地进行了更新。V8引擎负责将JavaScript代码编译为机器码并执行。升级后...

    vue微商城项目源码-vue-shop(适用于Node.js 10.x).zip

    Vue微商城项目源码分析与搭建指南 Vue.js是一个轻量级、高性能的前端JavaScript框架,它具有组件化、响应式、易于上手的特点。在本项目中,我们使用Vue.js构建了一个微商城应用,结合Node.js作为后端服务器,为用户...

    Web前端模块化开发教程(ES6+Node.js+Webpack)_源代码.zip

    3. **Node.js**:Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它让JavaScript可以在服务器端运行。在前端模块化开发中,Node.js用于搭建开发环境,安装和管理依赖,以及构建和打包工具。 4. **Webpack**...

    基于node.js的图书管理系统的设计与实现.docx

    Node.js是一个开源、跨平台的JavaScript运行环境,它允许开发者在服务器端运行JavaScript代码,具有高性能、非阻塞I/O和事件驱动的特性,适合构建实时、可扩展的网络应用。 2.2 MongoDB介绍 MongoDB是一种面向文档...

    Node.js-Nodecanvas一个Cairo支持NodeJS的Canvas实现

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境,它让开发者能够在服务器端使用 JavaScript 进行编程,极大地扩展了 JavaScript 的应用场景。而 NodeCanvas 是一个专为 Node.js 设计的 Canvas 库,它实现...

    Node.js-基于VUE2.x和Node.js的完整开源博客解决方案--ZOEBlog

    "Node.js-基于VUE2.x和Node.js的完整开源博客解决方案--ZOEBlog" 这个标题表明我们正在讨论一个开源的博客系统,它使用了Node.js作为服务器端技术,并结合了Vue.js 2.x版本作为前端框架。"ZOEBlog"可能是这个项目的...

Global site tag (gtag.js) - Google Analytics