Node.js源碼解析-啟動(dòng)-js部分

Node.js源碼解析-啟動(dòng)-js部分

歡迎來我的博客閱讀:《Node.js源碼解析-啟動(dòng)-js部分》

Node.js 版本 8.x

Node.js 進(jìn)程啟動(dòng)時(shí),首先執(zhí)行 c / c++ 代碼圣蝎,然后 c / c++ 加載并執(zhí)行 lib/internal/bootstrap_node.js 并給予一個(gè) process 參數(shù)( 運(yùn)行上下文 )

// 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 對(duì)象來自 c / c++宠漩,屬于原始數(shù)據(jù)
(function(process) {
  // ...

  startup();
})

加載 lib/internal/bootstrap_node.js 后嚣镜,直接執(zhí)行 startup() 函數(shù)

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')
    }));
    // 相當(dāng)于 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 對(duì)象的初始化操作
    // 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 的運(yùn)行模式,我們只關(guān)注 node xx.js 的運(yùn)行模式

    // if () {
    // ...
    } else {
      // 執(zhí)行用戶代碼

      // 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');
        // 變?yōu)榻^對(duì)路徑
        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() 最后調(diào)用 Module.runMain() 函數(shù)來加載并執(zhí)行用戶代碼形用。在執(zhí)行 startup() 函數(shù)的過程中恒削,多次用到了 NativeModule.require() 來加載模塊

NativeModule

NativeModule.require() 是專門用來加載 Node.js 內(nèi)置模塊的

// 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 有幾個(gè)重要的屬性和方法:

  • id: NativeModule 的標(biāo)識(shí)符,例如 events尾序,internal/process
  • filename: NativeModule 對(duì)應(yīng)源碼文件
  • exports: 默認(rèn)值是 {}
  • loaded / loading: NativeModule 狀態(tài)
  • _cache: 簡(jiǎn)單的模塊緩存
  • _source: 模塊源碼資源
  • require(): 先查詢緩存钓丰,緩存沒有則新建 NativeModule 并編譯,返回 exports
  • wrap()/wrapper: wrapper 是對(duì)模塊上下文的包裹每币,如下:
    (function (exports, require, module, __filename, __dirname) { 
      xxx
    });
    
  • compile(): 將模塊源碼用 wrapper 包裹后携丁,使用 runInThisContext()(類似 eval())生成 js 函數(shù),再執(zhí)行之

Module.runMain()

Node.js 啟動(dòng)完成后兰怠,調(diào)用 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() 加載并執(zhí)行用戶代碼。至此 啟動(dòng)-js部分 已經(jīng)全部完成揭保,后續(xù)模塊加載部分肥橙,見 Node.js源碼解析-require背后

End

啟動(dòng)只是 Node.js 源碼的一小部分,除此之外還有大量的內(nèi)置模塊和 c / c++ 源碼

參考:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末秸侣,一起剝皮案震驚了整個(gè)濱河市存筏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌味榛,老刑警劉巖椭坚,帶你破解...
    沈念sama閱讀 221,548評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異搏色,居然都是意外死亡善茎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門频轿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來垂涯,“玉大人烁焙,你說我怎么就攤上這事「福” “怎么了骄蝇?”我有些...
    開封第一講書人閱讀 167,990評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)鞠苟。 經(jīng)常有香客問我乞榨,道長(zhǎng),這世上最難降的妖魔是什么当娱? 我笑而不...
    開封第一講書人閱讀 59,618評(píng)論 1 296
  • 正文 為了忘掉前任吃既,我火速辦了婚禮,結(jié)果婚禮上跨细,老公的妹妹穿的比我還像新娘鹦倚。我一直安慰自己,他們只是感情好冀惭,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評(píng)論 6 397
  • 文/花漫 我一把揭開白布震叙。 她就那樣靜靜地躺著,像睡著了一般散休。 火紅的嫁衣襯著肌膚如雪媒楼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評(píng)論 1 308
  • 那天戚丸,我揣著相機(jī)與錄音划址,去河邊找鬼。 笑死限府,一個(gè)胖子當(dāng)著我的面吹牛夺颤,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播胁勺,決...
    沈念sama閱讀 40,819評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼世澜,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了署穗?” 一聲冷哼從身側(cè)響起寥裂,我...
    開封第一講書人閱讀 39,725評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蛇捌,沒想到半個(gè)月后抚恒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,268評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡络拌,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了回溺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片春贸。...
    茶點(diǎn)故事閱讀 40,488評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡混萝,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出萍恕,到底是詐尸還是另有隱情逸嘀,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評(píng)論 5 350
  • 正文 年R本政府宣布允粤,位于F島的核電站崭倘,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏类垫。R本人自食惡果不足惜司光,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望悉患。 院中可真熱鬧残家,春花似錦、人聲如沸售躁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)陪捷。三九已至回窘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間市袖,已是汗流浹背啡直。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留凌盯,地道東北人付枫。 一個(gè)月前我還...
    沈念sama閱讀 48,897評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像驰怎,于是被迫代替她去往敵國(guó)和親阐滩。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容