-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
node源码粗读(1):一个简单的nodejs文件从运行到结束都发生了什么 #5
Comments
厉害厉害.jpg; 坐等更新 |
暂时停更,今天不小心rm -rf 删掉了测试用的node源码。本来准备下一份最新的代码继续解析,但是master分支以及node-v8.x出现 ./configue --debug 报错问题,无法生成调试用的node应用程序。详情请查看 issue 待pr merge到master后开更 |
已更完。 |
你好,make -j 4生成 |
@nickleefly configure 的时候加上 --debug参数,这样在make的时候会生成out/Debug文件夹,选取out/Debug/node做为可执行文件进行调试,参数就是你要跑的js文件(相当于out/Debug/node xx.js) ,之后在node源码文件中打断点并进入ide的debug模式就好了 |
@xtx1130 谢谢 👍 |
@nickleefly 不客气😆 |
@nickleefly 去掉Before launch Build 一栏中的Build |
@xtx1130 在单位电脑才有CLion编辑器,可以了,谢谢 👍 |
@nickleefly 不客气😆 |
你好,我想知道为什么我lib文件下所有js下断点都无效,只有test.js下断点有效 |
@xushuwei202 看下这篇文章#14 |
一、配置ide和node编译
对ide的配置和node编译的过程这里不赘述了,如果有时间,可能写一篇blog简单介绍一下。
二、node运行入口粗读
1. argc和argv
这两个在之后的代码里会经常见到,argc表示的是命令行参数个数,而argv表示的是命令行的参数,如果没有参数那么就是node的运行的绝对地址
2.platformInit运行
node运行的时候首先要走的便是platformInit方法,这个方法主要是对运行平台的一些参数进行初始化:
![issue5](https://github.com/xtx1130/blog/raw/master/images/issue5/issue5-2.png)
可以看到:首先有对信号的处理(sig*),还有对系统的stdin、stdout的检测(STDIN_FILENO,STDERR_FILENO),这里截图没截全,就不一一介绍了。
3.调用node::performance::performance_node_start
这里从字面就很好理解,开始对node性能进行记录,这里面有个要注意的地方是:它底层其实调用的是uv__hrtime。node有很多可以计算时间的方法,大家如果读读源码,就会发现,所有计算耗时的方法都绕不开这个api。
4.调用uv_setup_args
对uv_setup_args调用主要要解决的问题就是调用uv_malloc对argv的副本new_argv分配内存,并返回。说白了就是复制一份argv给process.title这个api用。
5.调用Init方法
大家可以看到这个Init有四个参数,argc和argv刚才已经介绍过了,而后面两个参数分别给argc和argv加上了exec前缀,经过阅读就会发现,exec前缀就是待执行状态的argc和argv。在Init函数体内,主要做的事情有以下几个:
node建立起openSSL_CONF,而这个CONF来自你的参数--openssl-config
6.判断openSSL
这里没什么好说的,就是判断openSSL,如果有的话读取ca,为用openSSL通讯做准备。
7. v8_platform.Initialize初始化
v8_platform.Initialize,这里主要对libuv线程池做初始化操作。
8.V8::Initialize()初始化
接线来就是重头戏了,对v8进行初始化操作,其中又囊括了很多点,我在下面一一列出,里面一些概念也会在列出的时候提及一下:
在这里有一个比较重要的概念,就是OnceType,这是node作者定义的一种类型,如果翻一下源码可以翻到一个宏定义 V8_DECLARE_ONCE,这是专门用来声明OnceType类型的,对于只需要在创建的时候声明一次的都会定义为OnceType,通过全局搜索就很容易找出来OnceType:
![issue5](https://github.com/xtx1130/blog/raw/master/images/issue5/issue5-4.png)
v8::internal::V8::InitializeOncePerProcessImpl,这个函数通过名字就能很好理解,就是只在进程运行时候初始化一次的接口,也就是上述的OnceType,咱们接着往下看。
9.调用node::performance::performance_v8_start
这似曾相识的代码就不过多解释了,就是对v8进行性能记录,底层同样调用的uv_hrtime。
10.调用内联Start
环境搞定了,接下来就是开始运行咯。下面我们来看看这个start都做了哪些事情
看到它第一个参数,就知道了,先初始化uv_default_loop,就不再过多解释了,有问题的可以翻翻libuv,很好理解,下面贴一下初始化的代码:
接下来是真正的start了,在这里贴一下代码,然后详细解释一下:
这句话表面上是给isolate添加监听,而如果你深入去研究的话就会发现,它是通过HandleScope来监听javascript对象内存的变化并发出相应的message。
这里要做的是对未捕获的异常进行监听(可能对libuv中的问题进行了监听,具体没有深入看,之后会进行确认)。
三、node执行过程中发生了什么
1.node::Environment::Start
这个函数对node运行时候的环境做了初始化,其中初始化了大家非常熟悉的HandleScope,Context,下面还有对uv_idle_init以及uv_timer_init等初始化的操作,这里就不过多介绍了。
2.async_hooks和LoadEnvironment
接下来注意这段代码:
这里引入了一个新的概念就是async_hooks,从这段代码就能看出来async_hooks是如何记录整个生命周期的,因为他是在生命周期之前推入,而又是在生命周期之后推出的。
LoadEnvironment则是整个运行时候的环境,接下来会讲解到LoadEnvironment到底都做了什么。
3.node::LoadEnvironment
在这里重点说一下很有名的bootstrap_node.js,这是外部js文件的入口,外部js文件进来之后要经过几个主要的步骤:通过vm.script 校验代码;通过preloadModules()预加载模块;通过module.rumMain()执行外部js文件。
在内存分配的时候,有个小知识点,正好在这里提一下:
大家可以注意一下这段代码,相信懂英文的人都能看懂,这里是对大对象进行判断,如果字节超过kMaxRegularHeapObjectSize则会被分配到LO_SPACE中。在这里给大家解释一下这个space,node中做堆内存分配的space细分总共五类,分别是:
其中NEW_SPACE的内存是连续的,OLD_SPACE和MAP_SAPCE则是基于页进行管理的,存放不下的话会不断新加内存页进来,直到max_size。LO_SPACE则是有单独的存储空间,也是基于页进行管理(所以粗分其实只有三类)。
下面贴一部分OLD_SPACE内存分配代码:
还有一个知识点,就是node内置模块是如何加载的,在这里不做展开讨论了,网上资料很多,请自行查阅。
四、运行结束
运行结束之后的工作就做过多解释了,大家简单看下代码直接过了,无非是一些扫尾的工作:
五、总结
至此,整体流程也就大致清晰了,文章比较干,还是希望大家真正上手调试走一遍流程,看一下代码,这样印象才能更深刻,如果文章中有说的不正确的地方,也请大神在评论中进行指正。
by 小菜
The text was updated successfully, but these errors were encountered: