歡迎來個人博客閱讀:《Node.js源碼解析-啓動-js部分》node
Node.js 版本 8.xc++
Node.js 進程啓動時,首先執行 c / c++ 代碼,而後 c / c++ 加載並執行 lib/internal/bootstrap_node.js
並給予一個 process
參數( 運行上下文 )git
// 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()
函數github
// 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()
來加載模塊bootstrap
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
有幾個重要的屬性和方法:app
id
: NativeModule
的標識符,例如 events
,internal/process
ide
filename
: NativeModule
對應源碼文件函數
exports
: 默認值是 {}
ui
loaded
/ loading
: NativeModule
狀態
_cache
: 簡單的模塊緩存
_source
: 模塊源碼資源
require()
: 先查詢緩存,緩存沒有則新建 NativeModule
並編譯,返回 exports
wrap()/wrapper
: wrapper
是對模塊上下文的包裹,以下:
(function (exports, require, module, __filename, __dirname) { xxx });
compile()
: 將模塊源碼用 wrapper
包裹後,使用 runInThisContext()
(相似 eval()
)生成 js
函數,再執行之
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部分
已經所有完成,後續模塊加載部分,見 Node.js源碼解析-require背後
啓動只是 Node.js 源碼的一小部分,除此以外還有大量的內置模塊和 c / c++ 源碼
參考: