node源码详解(二 )—— 运行机制 、整体流程

5822b4883a48ad366440e4f1 Luzeshu 星期六, 2016年3月12日 26

本作品采用知识共享署名 4.0 国际许可协议进行许可。转载联系作者并保留声明头部与原文链接https://luzeshu.com/blog/nodesource2
本博客同步在https://cnodejs.org/topic/56e3be21f5d830306e2f0fd3
本博客同步在http://www.cnblogs.com/papertree/p/5225201.html


2.1 项目代码结构

node 主要的部分有4个【下图最左列就是node项目源码(v4.2.2)的根目录】:

  1. 原生 js模块:node提供给 用户js 代码的类接口,平时用的require('fs')、require('http')调用的都是这部分的代码。【最左列的 lib文件夹,展开后是左二列】
  2. node 源码:node程序的main函数入口;还有提供给lib模块的C++类接口。【最左列的 src 文件夹,展开后是第三列】
  3. v8引擎:node用来解析、执行js代码的运行环境。【最左列的deps文件夹展开后是第四列,v8和libuv等依赖都放在这里】
  4. libuv:事件循环库,提供最底层的 io操作接口(包括网络io操作的epoll_wait()、文件异步io的线程池管理)、事件循环逻辑。 【第四列的uv文件夹,展开后是第五列】

记住这几个路径:

./lib ./src ./deps/uv

2-1-1.png

图2-1-1


2.2 运行流程

下图4个红色序号分别对应着上篇博客提出的4个问题所在位置。后续博客分说。

接下来对一些关键地方进行说明:

1. 核心数据结构 default_loop_struct (结构体为struct uv_loop_s,后续祥讲)

这个数据结构是事件循环的核心。当node执行到“加载js文件”这个步骤(结合下图)时,用户的js代码如果有io操作: 那么js代码通过调用 -》lib模块(2.1 中的原生js模块)-》C++模块(2.1中的源码部分) -》 libuv接口(2.1中deps/uv部分) -》最终的系统api,拿到系统返回的一个fd(文件描述符),和 js代码传进来的回调函数callback,封装成一个io观察者(一个uv__io_s类型的对象),保存到default_loop_struct;


2. 进入事件循环

当处理完 js代码,如果有io操作,那么这时default_loop_struct是保存着对应的io观察者的。

处理完js代码,main函数继续往下调用libuv的事件循环入口uv_run(),node进程进入事件循环:

uv_run()的while循环做的就是一件事,判断default_loop_struct是否有存活的io观察者。
a. 如果没有io观察者,那么uv_run()退出,node进程退出。
b. 而如果有io观察者,那么uv_run()进入epoll_wait(),线程挂起等待,监听对应的io观察者是否有数据到来。有数据到来调用io观察者里保存着的callback(js代码),没有数据到来时一直在epoll_wait()进行等待。

这里解答了博客(一)的“问题2”的一部分:为什么console.log()的js代码导致node退出,而server.listen(80)导致线程挂起等待。


3. 这里一旦没搞清逻辑就有个疑问:

第2点说在uv_run()里面如果监听的io观察者有数据到来,那么调用对应的callback,执行js代码。如果没有数据到来,一直在epoll_wait()等待。那如果我js代码里面有新的 io操作想要交给epoll_wait()进行监听,而此刻监听着的io观察者又没有数据到来,线程一直在这里等待,那怎么办?

首先第1点讲到,执行js代码的时候,通过调用node提供的C++接口最终把io观察者都保存到default_loop_struct里面,js代码执行完之后,node继续运行才进入epoll_wait()等待。也就是说node在epoll_wait()的时候,js代码执行完毕了。而js代码的回调函数部分,本来的设定就是在epoll_wait()监听的io观察者被触发之后才会执行回调,在epoll_wait()进行等待的时候,不可能存在“有新io操作要交给epoll_wait()去监听”这样的js代码。

2-2-1.png

图2-2-1
26 Comments:
9146ca2e-c5f1-42a4-9aec-e421cdd0167d

2016-03-13 10:57:58


2016-03-13 21:12:14

@yongningfu 第一条评论T T


2016-03-14 00:01:24

@bigtree9307 特别赞那么将自己的见解分享的人 这也是我喜欢node社区的一个原因,现在在读nodeclub码源,过段时间把你的node源码分享来仔细的看一下

8ec9ad97-dc4b-45d1-84cd-74012821ec1a

2016-03-20 10:14:16

编辑了markdown版本方便这边看~

2ddb7b59-415f-4e85-aa19-ea783d3f6797

2016-03-29 16:06:56

看了好几遍了,今天有点豁然开朗的感觉


2016-06-21 10:24:01
d9251ca5-64e7-44b7-9d84-0c07c6091b72

2016-05-24 17:31:12

大神,收徒么?呜哇~呜哇~


2016-06-21 10:24:13

@huihongzhou 不敢当哈

8902869d-5b4d-410c-a9ec-7ef93f2df9d3

2016-06-10 11:30:09

太赞了,讲得很清晰,很有条理,看完之后豁然开朗


2016-06-21 10:24:28
5749f4aa-1459-4315-867b-06a85ee61794

2016-07-26 21:29:44

啥都憋说了,阅读帖子之前先膜拜大神。哈哈哈


2016-07-26 21:40:55

@vincentgor 离大神还差很远哈哈

6911f124-ab09-4e67-a9d0-01d093504bbe

2016-07-27 20:45:16

35c5cab9-580e-46a5-9981-f47a87ab68ae

2016-07-28 09:27:36

mark一下 最近node的js的一些热门npm库源码看了不少 想花点时间研究下node的底层实现,我想问下4.x的源码和您写的还一致嘛… 因为项目使用的node版本就是4.x,所以想研究下这个版本的node源码实现


2016-07-28 23:14:45

@hyj1991 是基于node4.2.2版本的源码

27807e56-67b5-4d95-b33f-3e818eafefcf

2016-07-28 15:18:08
ec6551bd-4f77-4295-8162-f35786fe662a

2016-07-29 14:59:11

@bigtree9307 好的,非常感谢哇,收藏了

096ce340-82af-4085-8a45-2463f42456d0

2016-07-31 16:42:25

关注下。。。

7dd4c24f-4433-4257-8224-b408036d0641

2016-07-31 19:21:54

mark

b79c7326-0320-4bea-bb21-9edce0ed8275

2016-09-11 11:47:09

想问一下main函数在哪个文件呢?


2016-09-19 16:52:23

@蓝浩
可以全局搜一下呢,4.2.2版本的是在src/node_main.cc

04c3c2bc-e4ee-4f81-89ff-45fbe4a0c13a

2016-09-26 14:12:57

文末的图其实第一次看的一知半解的 后来按照你的6个教程仔细看了下对应的源代码实现,现在再来看这个图,画的非常清晰 再赞一个?

58c965979b325705b95c2713
2017-03-16 00:02:31

感谢!博主分析的很透彻!!

c047c1e6-a629-48e4-be05-bbed92ae4f38

2017-07-05 16:40:30

太赞了,图太清晰了

f6d7af47-b3c1-4010-94e1-2b5d7c924ecf
2017-11-15 09:33:56

博主,这么清晰的思路,有深度的文章,有没参考什么文献书籍或教程,可否参考下?

2017-11-15 10:07:13

没有呢,就是啃源码,知识点都是零碎学习的有些是积累的,之前也是啃了好几个月

声明:电子邮箱用于接收回复的通知邮件(来自<mail@luzeshu.com>,请勿回复至此邮箱)。
163邮箱可正常接收,gmail邮箱注意查看垃圾邮件,qq邮箱由于ip频率限制可能无法成功投递。