未加星标

Node.js异步IO实现浅析

字体大小 | |
[前端(javascript) 所属分类 前端(javascript) | 发布者 店小二03 | 时间 2017 | 作者 红领巾 ] 0人收藏点击收藏

其实就是对 Node.js 的异步 IO 很感兴趣,加之最近可能要定制 Node.js ,所以决定研究研究看看。本身是 C/C++ 出身,看这点代码还是轻车熟路的,分析中并没有涉及 V8 的内部实现。

版本: e116cbe3207a471b3d604466baad49b141e32230

入口点

因为是要研究研究异步 IO ,我觉得从 fs 模块下手是最简单的了。源码通过 Git 克隆下来以后,直觉告诉我 fs 模块的源码入口点在 lib 里面。这里我从 fs.readFile 开始下手。

层层深入 - JS层

基于我克隆的版本的这个函数定义是在 fs.js 的 253 行,代码如下:

fs.readFile = function(path, options, callback){ callback = maybeCallback(arguments[arguments.length - 1]); options = getOptions(options, { flag: 'r' }); if (!nullCheck(path, callback)) return; var context = new ReadFileContext(callback, options.encoding); context.isUserFd = isFd(path); // file descriptor ownership var req = new FSReqWrap(); req.context = context; req.oncomplete = readFileAfterOpen; if (context.isUserFd) { process.nextTick(function(){ req.oncomplete(null, path); }); return; } binding.open(pathModule._makeLong(path), stringToFlags(options.flag || 'r'), 0o666, req); };

这段代码的逻辑不解释了基本都看得明白,最后的调用 binding.open 的是原生调用,实现基于 C++ ,具体因为不是关注重点我直接忽略了。

关于 fd 的判断直接忽略,我们关注到创建的 FSReqWrap 的 context 是一个 ReadFileContext 实例;oncomplete 指向一个读文件的回调,进入它可以看到:

function readFileAfterOpen(err, fd) { var context = this.context; if (err) { context.callback(err); return; } context.fd = fd; var req = new FSReqWrap(); req.oncomplete = readFileAfterStat; req.context = context; binding.fstat(fd, req); } function readFileAfterStat(err, st) { var context = this.context; if (err) return context.close(err); var size = context.size = st.isFile() ? st.size : 0; if (size === 0) { context.buffers = []; context.read(); return; } if (size > kMaxLength) { err = new RangeError('File size is greater than possible Buffer: ' + `0x${kMaxLength.toString(16)} bytes`); return context.close(err); } context.buffer = Buffer.allocUnsafeSlow(size); context.read(); }

这里直接我跳过两个方法的分析, 但是要注意 this 的指向和 context 的传递 0.0 ,最后我们看到了 context.read ,context 是一步一步传递下来的 ReadFileContext 实例,我们进入它的定义看看:

ReadFileContext.prototype.read = function() { var buffer; var offset; var length; if (this.size === 0) { buffer = this.buffer = Buffer.allocUnsafeSlow(kReadFileBufferLength); offset = 0; length = kReadFileBufferLength; } else { buffer = this.buffer; offset = this.pos; length = this.size - this.pos; } var req = new FSReqWrap(); req.oncomplete = readFileAfterRead; req.context = this; binding.read(this.fd, buffer, offset, length, -1, req); };

最终我们还是遇到了 binding.read 。这个调用之前的逻辑我相信大家看得懂,我们开始进入 C++ 的世界了 ==

层层深入 - C++层

这段代码定义在哪呢?我不知道各位有木有研究过 node 的 native 模块定义,其实这段代码很好找,过程不说了文件其实是:node_file.cc

基于我克隆的版本,绑定在 1457 行, 定义在 1192 行,最后调用了一个宏:ASYNC_CALL ,我们看到注释:

Wrapper for read(2). bytesRead = fs.read(fd, buffer, offset, length, position) 0 fd integer. file descriptor 1 buffer instance of Buffer 2 offset integer. offset to start reading into inside buffer 3 length integer. length to read 4 position file position - null for current position

可能会引起误解,这里的意思是接口兼容 read(2) 实现,但是其实不是基于read(2) ,而是使用宏 ASYNC_CALL 方式调用,我们深入 ASYNC_CALL 研究到它是 ASYNC_DEST_CALL 的宏,而 ASYNC_DEST_CALL 定义的内容如下:

#define ASYNC_DEST_CALL(func, request, dest, encoding, ...) \ Environment* env = Environment::GetCurrent(args); \ CHECK(request->IsObject()); \ FSReqWrap* req_wrap = FSReqWrap::New(env, request.As<Object>(), \ #func, dest, encoding); \ int err = uv_fs_ ## func(env->event_loop(), \ req_wrap->req(), \ __VA_ARGS__, \ After); \ req_wrap->Dispatched(); \ if (err < 0) { \ uv_fs_t* uv_req = req_wrap->req(); \ uv_req->result = err; \ uv_req->path = nullptr; \ After(uv_req); \ req_wrap = nullptr; \ } else { \ args.GetReturnValue().Set(req_wrap->persistent()); \ }

别告诉我 ## 和 # 宏定义你不认识,因为我发现我周围基本没几个人认识(可能我们一群菜鸡Orz…),其实按照当前的层次深入,就是调用了 uv_fs_read ,可知这是一个 libuv 提供的接口。

不过我们发现,其提供的 event_loop 来自参数作用域,我们想深入探究一下其作用域,根据调用栈回溯一下得到参数来自 fs.js

本文前端(javascript)相关术语:javascript是什么意思 javascript下载 javascript权威指南 javascript基础教程 javascript 正则表达式 javascript设计模式 javascript高级程序设计 精通javascript javascript教程

主题: Node.jsC++Git其实
分页:12
转载请注明
本文标题:Node.js异步IO实现浅析
本站链接:http://www.codesec.net/view/529794.html
分享请点击:


1.凡CodeSecTeam转载的文章,均出自其它媒体或其他官网介绍,目的在于传递更多的信息,并不代表本站赞同其观点和其真实性负责;
2.转载的文章仅代表原创作者观点,与本站无关。其原创性以及文中陈述文字和内容未经本站证实,本站对该文以及其中全部或者部分内容、文字的真实性、完整性、及时性,不作出任何保证或承若;
3.如本站转载稿涉及版权等问题,请作者及时联系本站,我们会及时处理。
登录后可拥有收藏文章、关注作者等权限...
技术大类 技术大类 | 前端(javascript) | 评论(0) | 阅读(56)