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


  在上一篇博客(详解四)讲了 C++通过v8的Object类和FunctionTemplate类,创建对象、方法,设置属性、原型方法等,提供给运行时的 js代码调用。

  那么这些C++实现的process对象、TCP类是否都在程序启动的时候就创建到 js的执行环境(context)呢?

  不全是。process对象是(见5.2节),但 TCP类等C++内建模块不是(见5.3节)。  

5.1 在main函数启动之前 —— C++内建模块的注册

  看一下C++内建模块的实现方法:

图5-1-1

  我们看到最后一行的NODE_MODULE_CONTEXT_AWARE_BUILTIN,通过这个方式来导出一个C++内建模块,这行代码实现了什么?

5.1.1 NODE_MODULE_CONTEXT_AWARE_BUILTIN 宏

  看下这个宏的实现,在src/node.h文件:

图5-1-2

5.1.2 NODE_C_CTOR宏与__attribute__((constructor))声明 —— 在main函数前注册

  看到NODE_C_CTOR这个宏的作用时给传进来的函数加上这么一个声明,这是gcc的一个函数属性声明。

  来自gcc文档的说明:(地址:https://gcc.gnu.org/onlnedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes

    [ The constructor attribute causes the function to be called automatically before execution enters main (). ]

     [笔者译:constructor属性导致该函数在程序进入main函数之前被执行]  

  注:上面的 “.CRT$XCU”是微软的编译器实现类似功能的方法。

5.1.3 node_module_register函数 与 node::node_module结构体的链表modlist_builtin

  通过图5-1-2,可以知道通过NODE_MODULE_CONTEXT_AWARE_BUILTIN( tcp_wrap, TCPWrap::Initialize )宏去注册一个C++内建模块时,过程如下:

  1. 把 tcp_wrap(modname)和 Initilize函数(regfunc)封装成一个node_module类型的结构体 _module

  2. 调用node_module_register(&_module) 函数进行注册。

图5-1-3

  可以看到这个函数把传进来的m插入到内建模块的链表modlist_builtin。

5.1.4 总结

  通过5.1节,我们知道tcp_wrap.cc里面实现的C++内建模块,在main函数启动之前把该内建模块的初始化函数 TCPWrap::Initialize()保存到一个全局的静态链表modlist_builtin。

  那么这些内建模块的Initialize()什么时候执行呢?里面可是创建了TCP类对应的FunctionTemplate呢,在创建对应的FunctionTemplate对象之前,V8的context里头,js代码还是没得直接使用new TCP()呢。


5.2 process.binding —— C++内建模块的初始化与缓存

  既然C++内建模块只是保存初始化函数到链表,而不真正创建一个js可以使用的函数对象(TCP类)到v8的上下文(context),那么初始化这个步骤明显要交给 js代码来控制了。

  通过process.binding('tcp_wrap') 来引入C++内建模块创建的对象,如果第一次binding这个内建模块,那么就会调用Initialize函数。

  那么你可能会问,C++内建模块就需要 js里面调用process.binding()来引入,那process对象也是C++提供的v8::Object类型的对象,为什么可以直接使用process.binding()呢?

  因为内建模块并不是你js代码一定会用到的,所以通过保存TCPWrap::Initialize()的方式,等到要用到时再通过process.binding()初始化。你要一开始就执行初始化函数,也是可以创建对应的FunctionTemplate对象到v8上下文的。

  而process这个对象在main函数启动之后、执行node.js之前就创建了,那么js代码运行的上下文(context)里面就可以直接用了。具体代码见5.3节。

  来看一下process.binding()的过程:

  [注:C++设置给process对象的binding方法是下面的Binding()函数]

图5-2-1

  1. Binding()函数通过要binding的模块名,调用get_builtin_module(),从5.1.3中提到的内建模块链表modlist_buildin去获取node::node_module结构体对象

  2. node_module对象里面的nm_context_register_func字段保存着对应的初始化函数(即5.1.3中保存的TCPWrap::Initialize),图中第2320行可以看出binding的时候执行了初始化函数。

  3. 最后把exports当成process.binding()的返回值。exports哪里被赋值呢?注意到nm_context_register_func其实是tcp_wrap.cc里面的TCPWrap::Initialize()函数,回去图5-1-1看看这个函数,就会发现哦原来就是target。


5.3 process对象的初始化

  在3.1.2节中讲到这么一个流程:

    main() -> Start() -> StartNodeInstance() -> LoadEnviroment()

  在LoadEnvrionment()里面去执行node.js文件,并把process对象传进去。

5.3.1 process初始化(js部分)

  在LoadEnvrionment()执行node.js文件,并传process进去的时候,process.nextTick()这些函数是在node.js里面初始化的。

5.3.1 process初始化(C++部分)

  在main() -> Start() -> StartNodeInstance() 这里,执行LoadEnvironment()之前,先调用了CreateEnvironment()。

  在CreateEnvironment()里面创建process对象,并且初始化C++部分的函数。看代码:

图5-3-1

  1. 创建v8::Object process_object 在 CreateEnvironment()里面。

  2. 给process.binding等赋值在SetupProcessObject()里面。

node源码详解(五)的更多相关文章

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

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource2 本博客同步在https://cnodejs.o ...

  2. node源码详解(五) —— 在main函数之前 —— js和C++的边界,process.binding

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource5 本博客同步在https://cnodejs.o ...

  3. node源码详解(四) —— js代码如何调用C++的函数

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource4 本博客同步在https://cnodejs.o ...

  4. node源码详解(三)—— js代码在node中的位置,process、require、module、exports的由来

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource3 本博客同步在https://cnodejs.o ...

  5. node源码详解 (一)

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource1 本博客同步在https://cnodejs.o ...

  6. node源码详解(三)

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource3 本博客同步在https://cnodejs.o ...

  7. node源码详解(四)

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource4 本博客同步在https://cnodejs.o ...

  8. OkHttp3源码详解(五) okhttp连接池复用机制

    1.概述 提高网络性能优化,很重要的一点就是降低延迟和提升响应速度. 通常我们在浏览器中发起请求的时候header部分往往是这样的 keep-alive 就是浏览器和服务端之间保持长连接,这个连接是可 ...

  9. node源码详解(七) —— 文件异步io、线程池【互斥锁、条件变量、管道、事件对象】

    本作品采用知识共享署名 4.0 国际许可协议进行许可.转载保留声明头部与原文链接https://luzeshu.com/blog/nodesource7 本博客同步在https://cnodejs.o ...

随机推荐

  1. 安卓Recovery模式该怎么用?【转】

    本文转载自:http://android.baike.com/article-109914.html 安卓系统出了名的刷机刷机再刷机,说起刷机就不能不谈Recovery模式,这项刷机过程中最重要的一到 ...

  2. Get-Acl 查看文件权限

    https://blogs.msmvps.com/erikr/2007/09/26/set-permissions-on-a-specific-service-windows/ Get-Acl .\L ...

  3. 0.0.0.0 IPAddress.Any 【】127.0.0.1 IPAddress.Loopback 【】localhost

    0.0.0.0  IPAddress.Any https://msdn.microsoft.com/en-us/library/system.net.ipaddress.any(v=vs.110).a ...

  4. bzoj 1093 [ ZJOI 2007 ] 最大半连通子图 —— 拓扑+DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1093 先缩点,然后就是找最长链,DP一下即可: 注意缩点后的重边!会导致重复计算答案. 代码 ...

  5. bzoj1231 [Usaco2008 Nov]mixup2 混乱的奶牛——状压DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1231 小型状压DP: f[i][j] 表示状态为 j ,最后一个奶牛是 i 的方案数: 所以 ...

  6. Organize Your Train part II(hash)

    http://poj.org/problem?id=3007 第一次用STL做的,TLE了,自己构造字符串哈希函数才可以.. TLE代码: #include <cstdio> #inclu ...

  7. DNS(域名系统)

    DNS(Domain Name System),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而不用去记住能够被机器直接读取的Ip数串.通过主机名,最终得到该主机 ...

  8. [Apple开发者帐户帮助]五、管理标识符(1)注册应用程序ID

    一个应用程序ID标识的配置设定档中的应用程序.它是一个由两部分组成的字符串,用于标识来自单个开发团队的一个或多个应用程序.有两种类型的应用程序ID:用于单个应用程序的显式应用程序ID,以及用于一组应用 ...

  9. C# 获取当月有多少天

    int days = DateTime.DaysInMonth ( 2009, 9 ); int days = DateTime.DaysInMonth ( DateTime.Now.Year, Da ...

  10. J2EE框架(Struts&Hibernate&Spring)的理解

    SSH:Struts(表示层)+Spring(业务层)+Hibernate(持久层)Struts:Struts是一个表示层框架,主要作用是界面展示,接收请求,分发请求.在MVC框架中,Struts属于 ...