tsy77

Results 20 issues of tsy77

在我们写node addon时,需要使用node-gyp命令行工具,大部分同学会用`configue`生成配置文件,然后使用`build`进行构建。但是node-gyp到底是什么?底层有什么呢?下面我们来刨根问底。 本文的线索是自底向上的讲解node-gyp的各层次依赖,主要有以下几个部分: 1. make 2. make install 3. cmake 4. gyp 5. node-gyp 层次结构如下图所示: ![](http://sf1-hscdn-tos.pstatp.com/obj/ies.fe.mis/c1de3ab23c966cf1013f02f04b97ec37.png) ## make 从源文件到可执行文件叫做编译(包括预编译、编译、链接),而make作为构建工具掌握着编译的过程,也就是如何去编译、文件编译的顺序等。 make是最常用的构建工具,针对用户制定的构建规则(makefile)去执行响应的任务。make会根据构建规则去查找依赖,决定编译顺序等。大致了解可参考[Make 命令教程](http://www.ruanyifeng.com/blog/2015/02/make.html) Makefile(makefile)中定义了make的构建规则,当然也可以自己指定规则文件。例如: ```` $ make -f rules.txt # 或者 $...

## 起因 起因是公司的某MIS系统中某个HTTP请求耗时长,有时达到几十秒,导致了xhr出发了onTimeout事件;chrome的network面板中,发现请求一直在stalled阶段。 ## 可能的原因 Stalled/Blocking的解释如下: > Time the request spent waiting before it could be sent. This time is inclusive of any time spent in proxy negotiation. Additionally, this...

本文我们将从源码角度来介绍V8引擎的内存管理部分,主要包括内存分配和垃圾回收。 为了聚焦思想,本文采用的V8比较低的版本0.1.5,这个版本实现起来比较简单,大家比较容易的看出实现思想。 ## 内存分配 V8将内存空间分为几个区域,分别是NewSpace、OldSpace、LargeObjectSpace、MapSpace、CodeSpace,各个space的关系如下图所示: ![](http://sf3-hscdn-tos.pstatp.com/obj/ies.fe.mis/b9aba83ec2f5dc5c8381aacd4a58aa1f.png) 各个space的作用: LargeObjectSpace :为了避免大对象的拷贝,使用该空间专门存储大对象(大小超过Normal Page能容纳的对象范围),包括Code、Sequetial String、FixedArray; MapSpace :存放对象的Map信息,即hidden_class;最大限制为8MB;每个Map对象固定大小,为了快速定位,所以将该空间单独出来; NewSpace :存放多种类型对象,最大限制为2MB; CodeSpace :存放预编译代码(?);最大限制为512MB; Old_Pointer_Space :存放GC后surviving的指针对象;最大限制为512MB; Old_Data_Space :存放GC后surviving的数据对象;最大限制为512MB; ### 初始化 首先是内存的初始化,这部分在V8初始化完OS的一些参数之后进行初始化,入口文件在src/heap.cc中。代码如下: ```` bool Heap::Setup(bool create_heap_objects) { //...

网上Vue源码解读的文章有很多,但涉及到Vuex、vue-router的就比较少了。本文主要对描述了Vue的整体代码结构,用两个例子分别描述Vue实例化及ssr的流程;接着阐述Vue插件的注册方法,Vuex、vue-router大致实现原理。 ## Vue ### 如何用例子走一遍Vue代码 1.clone vue代码到本地 2.在example目录中写上任意你想要测试的例子 3.npm run dev && node e2e/test/runner.js ### 目录结构 * root/ * compiler/--------------------解析template,生成render函数和ast * parser/-------------------正则遍历template里的字符串,通过栈记录元素关系,生成ast * codegen/-----------------根据ast生成render函数 * directives/---------------解析ast中的v-bind,v-model指令,生成对应render函数 * index.js * core/---------------------Vue实例相关,vue源码核心...

> Version > parcel-bundler: 1.11.0 ## 类 ![](http://lf1-hscdn-tos.pstatp.com/obj/ies.fe.mis/decffcfbd438a8d12a0d39fcead41505.png) Parcel中主要包含上述类: - Bundler,打包逻辑的入口 - Parser,Asset的注册表,根据文件后缀查找并创建对应的Asset类 - Asset,文件资源类,负责自身资源处理、依赖收集等操作,同时记录着原始资源、打包结果等信息;HTMLAsset、JSAsset等资源的Asset继承自此基类 - Bundle,打包输出文件类,它由多个资源(Asset)组成,会根据当前Bundle类的类型查找对应的打包器(从PackagerRegistry中获取),调用打包器的package方法将自身包含的Asset打包进目的文件;bundle可以有子bundle,当动态从该bundle导入文件的时候,或者导入一个其他类型资源的文件的时候会产生childBundles - PackagerRegistry,Packager注册表,根据资源类型(基本上是Bundle在调用,所以基本上是Bundle的类型,也可以说是对应Asset的类型)注册、获取打包器(Packager) - Packager,打包组合类,用于将各个Asset产生的结果打包进目标文件,比如JSPackager将类型为JS的Asset产生的内容,打包以Bundle.name为名字的文件中 - HMRServer,热更服务,其中包含启动ws服务,触发update等方法 - FSCache,缓存 - Resolver,资源路径解析类,如何对代码中引入的各种相对路径的资源路径进行解析,从而找到该模块的绝对路径 它们直接的调用及继承关系如下: - Bundler作为打包的入口,其中包含有Parser、Bundle、HMRServer、FSCache、Resolver等类...

1. 大小端模式 大小端模式是内存存储数据高低位的方式,对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如果将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。大小端只与CPU结构有关,与CPU硬件无关,CPU只负责程序运行,只有架构才管怎样取指令以及如何存储。 由于全局变量在编译阶段就会确定其内存分配,所以编译器还是需要知道是大端还是小端编译的,同理,操作系统也要知道大小端。 2. 二进制机器码为什么与操作系统有关 二进制的机器码按道理说是是CPU可以识别的指令,那么为什么同样的硬件环境,操作系统不同,二进制机器码不同呢?这是因为二进制机器码与OS的ABI相关,所以二进制机器码会随OS变化。 3. 计算机为什么要用补码存储 补码是在反码基础上加1得到的,反码的出现是为了解决“正负相加等于0”的问题,而补码的出现则是为了解决“只有一个0”的问题。 4. 软硬件在虚拟内存上是如何配合的 虚拟内存技术需要软硬件的协调,虚拟内存的这种缓存管理机制是通过操作系统内核,MMU(内存管理单元)中的地址翻译硬件和每个进程存放在主存中的页表(page table)数据结构来实现的。 页表(page table)是存放在主存中的,每个进程维护一个单独的页表。它是一种管理虚拟内存页和物理内存页映射和缓存状态的数据结构。它逻辑上是由页表条目(Page Table Entry, PTE)为基本元素构成的数组,数组的索引号对应着虚拟页号,数组的值对应着物理页号。数组的值可以留出几位来表示有效位,权限控制位。有效位为1的时候表示虚拟页已经缓存。有效位为0,数组值为null时,表示未分配。有效位为0,数组值不为null,表示已经分配了虚拟页,但是还未缓存到具体的物理页中。权限控制位有可读,可写,是否需要root权限 5. 为什么要有数据对齐 许多计算机系统对基本数据类型的合法地址作出了限制,要求某种类型对象的地址必须是K的倍数。这种限制或者说是建议旨在提高内存系统的性能。比如一个处理器每次总是从内存取8个字节,如果我们保证double类型的地址都是8的倍数,那么我们每次一个内存操作就可以读或者写值了。编译器为了达到数据对齐,会在struct的存储上插入间隙或在结尾加一些填充。 6. 栈帧中为什么要存储%rbp(基地址) 定长的栈帧在编译阶段就可以知道栈帧的长度,但对于变长的栈帧来说(比如函数调用alloca,alloca是在栈(stack)上申请空间)就需要%rbp来记录栈帧的基地址,在函数末尾执行leave命令,将栈指针%rsp保存成%rbp的位置,将%rbp弹出到%rbp寄存器。 7. 驱动程序原理 驱动程序连接着操作系统和设备控制器,是操作系统内核和机器硬件之间的接口。 用户进程利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数,这是linux的设备驱动程序工作的基本原理。 驱动程序调用int...

最近复习了一下编译原理,编译原理主要有以下几个阶段: 1.词法分析,将原文件中的字符分解为一个个独立的单词符号——TOKEN。词法分析的输入是字符流,输出的一个个词法单元()。 2.语法分析,分析程序的短语结构。语法分析从语法分析器输出的token中识别各类短语,并构造语法分析树。 3.语义分析,推算程序的含义。语义分析负责收集标志符的属性信息并存在符号表中;负责语义检查 4.中间代码生成 5.代码优化 6.目标代码生成 其中,语法分析、语义分析、中间代码生成三个阶段可以合为语法制导翻译。 本文将针对上述几个阶段进行简要介绍。 ## 词法分析 语法分析器从左到右的扫描程序中的字符,识别出各个单词,并确定单词类型,输出统一的词法单元token。 我们在做词法分析器时,主要遵循以下几个步骤: 1.确定Token的分类,比如关键字、常量、运算符、标志符、空格、注释等 2.为每一类token确定相应的正则及匹配函数 3.在主流程中逐一匹配正则并削减剩余字符串 我们以`sql-parser`中的词法分析部分为例: ### 正则及匹配函数 ```` WHITESPACE = /^[ \n\r]+/; Lexer.prototype.whitespaceToken = function() { var match,...

最近两天对electron应用一些性能分析,用到了VMtools、visual studio等工具。当然这里不是为了说这些,由于之前没有写过electron,对其实现也不了解,但在测试过程中发现了两个比较有趣的地方。 ### `electron .`和`electron main.js`启动时,appPath竟然不同 #### 表象如下: ![](http://sf6-hscdn-tos.pstatp.com/obj/ies.fe.mis/eb4513f6fc275a49e2d5ec54f899bf6b.png) 一个是`/Users/tsy/devspace/electron-quick-start/`,另一个则是`/Users/tsy/devspace/electron-quick-start/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar` #### 分析过程: 从后向前追溯electron源码,发现`appPath`在`App::SetAppPath`中被赋值。 ![](http://lf1-hscdn-tos.pstatp.com/obj/ies.fe.mis/7e4eb10eb16b536bfd11a3e501f8f237.png) 在electron中有两处调用了`App::SetAppPath`,一个是在`init.js`中,一个是在`default_app/mian.js`中。 `App::SetAppPath`第一次调用是在`init.js`中,`init.js`是electron初始化的逻辑,在Node中`bootstrap_node.js`中被加载,`init.js`中获取appPath的代码如下: ![](http://sf3-hscdn-tos.pstatp.com/obj/ies.fe.mis/56ba71555afe9deb52bd5be53241bb2b.png) 我们可以看出,其在Resource下分别找`app`、`default_app`、`default_app.asar`,也就是到目前为止,`appPath`为`/Users/tsy/devspace/electron-quick-start/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar` 我们在来看`App::SetAppPath`的第二次调用是在`default_app/mian.js`中,获取path的代码如下: ![](http://sf1-hscdn-tos.pstatp.com/obj/ies.fe.mis/8236e82ae34bb5a0038ecddd3c944698.png) 这里我们看到,如果执行目录中有`package.json`,则将当前目录设置成`appPath`;如果没有,则不会改变原有`appPath`。 #### 结论: 我们经过上述分析可以看出 - 当我们调用`electron .`时,表示当前目录,其中有`package.json`,所以当前目录被设置成`appPath` - 当我们调用`electron main.js`时,`main.js/package.json`不存在,则`appPath`保持`/Users/tsy/devspace/electron-quick-start/node_modules/electron/dist/Electron.app/Contents/Resources/default_app.asar`...

本文将讲解setTimeout、setImmediate和process.nextTick的实现,其中有一些部分与上一篇讲解的事件循环关联或依赖。 ## setTimeout 首先setTimeout在lib/internal/bootstrap/node.js中被初始化,代码如下: ```` function setupGlobalTimeouts() { const timers = NativeModule.require('timers'); global.clearImmediate = timers.clearImmediate; global.clearInterval = timers.clearInterval; global.clearTimeout = timers.clearTimeout; global.setImmediate = timers.setImmediate; global.setInterval = timers.setInterval; global.setTimeout = timers.setTimeout;...

本文将主要介绍libuv的事件循环,包括了事件循环的流程,而我们也知道libuv是使用poll机制来实现网络I/O,通过线程池来实现文件I/O,当然线程间也是通过poll机制来实现通信的,后面就将介绍线程池与事件循环是如何结合的。 ## event loop流程 事件循环的流程大致如下图所示: ![](http://sf3-hscdn-tos.pstatp.com/obj/ies.fe.mis/624c9768d8888b109a4649298c0cb091.png) 代码如下所示: ```` int uv_run(uv_loop_t* loop, uv_run_mode mode) { int timeout; int r; int ran_pending; // 有活跃的handle或req r = uv__loop_alive(loop); if (!r) uv__update_time(loop); while (r...