学习NodeJS第四天:初始化nodejs的历险之旅(上)

原文链接

nodejs 其源码大体上分  C/C++ 的和 JS 的,JS 文件主要集中在/lib目录里面,但别处 /src 中却有一个非常重要的 node.js(process.js) 文件,它是初始化 nodejs 的文件,在调试的时候也会经常断点在该源码上。本文基于 nodejs 0.2.0 的版本来围绕这份初始化文件谈谈对 nodejs 的认识。若不足之处,敬请提出!

nodejs的全局对象

相对于某些代码依赖于访问特定的包才能够使用的情况,nodejs 提供若干的全局对象(Global Objects)。也就是说,全局对象就可以免依赖某个包,任何时候任何地方都可以直接访问或使用那些全局对象。例如 nodejs 中一个很重要的全局对象 process,它代表在运行着的nodejs程序,并负责整个 nodejs 运行周期的调度。例如经常看到 console 也属于全局对象。可通过下面的代码让 console 配合 porcess 一起做 nodejs “空间的小型探针”。

  1. // 此时console也是一个全局对象(global.console = {};)  
  2. console.log('Version: ' + process.version); // nodejs版本号  
  3. console.log('Prefix: ' + process.installPrefix);// 安装目录  
  4. console.log('This process is pid ' + process.pid);// 系统进程id  
  5. console.log('This platform is ' + process.platform);// 运行平台  
  6. console.log(sys.inspect(process.memoryUsage()));// 使用内存的情况  
  7. // 分配nodejs内建的全局成员,/src/node.js第443行  
  8. global.require = require;  
  9. global.exports = self.exports;  
  10. global.__filename = filename;  
  11. global.__dirname = dirname;  
  12. global.module = self;  

global全局对象

因为JS里面全局对象同样也是一个特定对象,就叫作global(那是JS VM定义好的)作为关键字出现,所以最外层中一般用this访问即可(global==this)。(感谢fangzx的指正!一时疏忽混淆了Global)global 指的是最外层对象(global==this) 在初始化 nodejs 进行一开始中,反复获取和分配 global 引用(第3行开始):

  1. process.global.process = process;  
  2. process.global.global = process.global;  
  3. global.GLOBAL = global; // 所以 global 与GLOBAL是一样的,无分大小写,为啥写成那样?ry君喜欢嘛~@_@  
  4. global.root = global;  

另外,除了例子中的 console.log 外初始化过程中还提供了 consloe.info/dir/warn/trace 等的常规调试方法。

启动与退出nodejs

启动nodejs的这一环节从 process 的 API 定义的,process 此对象肩负起着许多重要的任务。我们一般从命令行输入 nodejs 的文件名执行程序,所以就要经过收集参数(读取process.argv数组)、实例化模块对象(创建Module类的实例)等等的步骤。下面 runMain 函数就是实例化模块的过程。观察源码第517行:

  1. // bootstrap main module.  
  2. exports.runMain = function () {  
  3.    // Load the main module--the command line argument.  
  4.    process.mainModule = new Module(".");  
  5.    process.mainModule.loadSync(process.argv[1]);  
  6. }  

但仍未开始!真正开始的地方是第764行:

  1. process.loop();  

实际上 loop 就是一个无限循环,无论那个 nodejs 程序有多复杂,任何一个 nodejs 程序都是从此循环处开始的。因为服务器一直在运行(守护进程的概念),所以表面上看,代码走到这里是停在这里的,恐怕不会走到最后一句 process.emit("exit");。若需要退出 nodejs 进程,则应使用 process.exit()emit(),exit() 源码如下(由此可见 exit 是事件来的):

  1. process.exit = function (code) {  
  2.   process.emit("exit");  
  3.   process.reallyExit(code);  
  4. };  

既然说到无限循环,那能不能挑明是哪个 loop、执行的是什么来着?我怀疑是 process._tickCallback 这个成员内部的 for loop,执行的内容保存在 nextTickQueue 队列数组中。具体的源码位置在47行起的一段。紧接着下面定义的 process.nextTick()是面向程序员的API,——文档里面也介绍过,这项清晰无误,然后这个 process._tickCallback 却是加了下划线的,难道是特殊成员??从命名上就不得不让人怀疑是让 V8 获取 loop 函数和 nextTickQueue 队列其引用而设的。也就是说 nextTickQueue 和 tickCallback  不是用于 JS 代码运行的,而是交到 V8/libev/libeio 内部去运行的。

  1. // nextTick()  
  2. var nextTickQueue = [];  
  3. process._tickCallback = function () {  
  4.   var l = nextTickQueue.length;  
  5.   if (l === 0) return;  
  6.   for (var i = 0; i < l; i++) {  
  7.     nextTickQueue[i]();  
  8.   }  
  9.   nextTickQueue.splice(0, l);  
  10. };  
  11. process.nextTick = function (callback) {  
  12.   nextTickQueue.push(callback);  
  13.   process._needTickCallback();  
  14. };  

坦白说,一方面况且自己是 C 鞋童……再深入也是头大,望路过诸位同好过问一下…… process.nextTick(callback) 用法就很简单,只是送入一个说明做什么的函数参数(函数类型),加入到 nextTickQueue 队列中,用法如下。据文档说并非 setTimeout(fn, 0) 一般能够达到之功效,且有效率得多。

  1. process.nextTick(function () {  
  2.    console.log('nextTick callback');  
  3. });  

下面则是 nextTick 的“反例”,摘自文档。

  1. process.on('exit'function () {  
  2.   process.nextTick(function () {  
  3.    console.log('不会运行这儿了'); // 因为已经结束循环,不会运行该函数。  
  4.   });  
  5.   console.log('About to exit.');   
  6. });  

包加载

引入自己写的包

……见《初始化nodejs的历险之旅(下)》

特殊的Script对象

……见《初始化nodejs的历险之旅(下)》

module机制如何工作?

……见《初始化nodejs的历险之旅(下)》

计时器Timer和其他

node.js 源码有为 POSIX 而考虑的事件,例如发生信号给进程的事件,在533行起。

node.js 源码还有定义计时器的部分(第577行)。计时器不算太复杂,主要是利用事件类完成事件的触发,须要依赖抽象的事件包 var events = module.requireNative('events')。

尚有的疑问

  1. 最外层的匿名函数 (function (process) {……}); 最后没有用于执行的括号??到底执行了没有??

  2. 其他的一些涉及Linux OS 原生层面之细节

小结

国庆在家,小弟这几天都在看 nodejs 源码。所以弄出此堆字,当然还是要以源码为底本。在本文中,主要了解了一下 nodejs 关键的几个部分,包括初始化全局成员、启动 node 进程、如何进行包加载、计时器、信号事件等的问题,涉及的对象主要是 process 及其 API。其中最后,仍有一些未确定的因素、一些存疑的地方,还摸不透,希望向大家请教。幸好 ry 君写得都是平易近人的 JS,比较起天书般的 C++  起码不用望C 兴叹、望而却步了,呵呵。当然,另一方面也反映出了 JS 作为脚本语言当是写 DSL 的用途。既然说到 JS 编码,一点印象就是,一看 nodejs 的源码没有太多的歧义等问题,真是平易近人,想必ry君自是行走于 C 与 JS 之间不但游刃,而且双管齐下,才写有如此清晰精湛的 nodejs 流,基本上阅读起来不会太吃力,好学,——也希望好用!。


阅读更多
个人分类: javascript
想对作者说点什么? 我来说一句

nodejs基础学习视频

2017年12月20日 55B 下载

nodeJS中文文档

2016年12月23日 4.26MB 下载

NodeJs api 中文版

2014年05月20日 514KB 下载

nodejs初学者入门

2015年11月16日 93KB 下载

深入浅出nodejs

2017年09月03日 44.64MB 下载

NodeJS开发指南高清版

2017年10月27日 9.69MB 下载

Nodejs入门经典

2016年10月20日 22.29MB 下载

NodeJS 安装包

2018年02月13日 15.7MB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭