Node.js架构剖析( 二 )

然而,其中用到的 fs.readFile 接口既不是 V8 提供的,也不是 JS 自带的,而是由 Node.js 以 C++ Binding 的形式借助 libuv 实现的:
// https://github.com/nodejs/node/blob/v14.0.0/lib/fs.js#L58const binding = internalBinding('fs');// https://github.com/nodejs/node/blob/v14.0.0/lib/fs.js#L71const { FSReqCallback, statValues } = binding;// https://github.com/nodejs/node/blob/v14.0.0/lib/fs.js#L297function readFile(path, options, callback) {callback = maybeCallback(callback || options);options = getOptions(options, { flag: 'r' });if (!ReadFileContext)ReadFileContext = require('internal/fs/read_file_context');const context = new ReadFileContext(callback, options.encoding);context.isUserFd = isFd(path); // File descriptor ownershipconst req = new FSReqCallback();req.context = context;req.oncomplete = readFileAfterOpen;if (context.isUserFd) {process.nextTick(function tick() {req.oncomplete(null, path);});return;}path = getValidatedPath(path);const flagsNumber = stringToFlags(options.flags);binding.open(pathModule.toNamespacedPath(path),flagsNumber,0o666,req);}最后的 binding.open 是一个 C++调用,用来打开文件描述符,三个参数分别是文件路径, C++ fopen 的文件访问模式串(如 r 、 w+ ),以及八进制格式的文件读写权限( 666 表示每个人都有读写权限),和接收返回数据的 req 回调
其中,internalBinding 是个 C++ binding loader,internalBinding('fs') 实际加载的 C++代码位于 node/src/node_file.cc
至此,关键的部分差不多都清楚了,那么,一段 Node.js 代码究竟是怎样运行的呢?
六.运行原理首先,编写的 JavaScript 代码由 V8 引擎来运行,运行中注册的事件监听会被保留下来,在对应的事件发生时收到通知
网络、文件 I/O 等事件产生时,已注册的回调函数将排到事件队列中,接着被事件循环取出放到调用栈上,回调函数执行完(调用栈清空)之后,事件循环再取一个放上去……
执行过程中遇到 I/O 操作就交给 libuv 线程池中的某个 woker 来处理,结束之后 libuv 产生一个事件放入事件队列 。事件循环处理到返回事件时,对应的回调函数才在主线程开始执行,主线程在此期间继续其它工作,而不阻塞等待
Node.js 就像一家咖啡馆,店里只有一个跑堂的(主线程),一大堆顾客涌过来的时候,会排队等候(进入事件队列),到号的顾客订单会被传给经理(libuv),经理将订单分配给咖啡师(worker 线程),咖啡师用不同的原料和工具(底层依赖的 C/C++模块)来制作订单要求的各种咖啡,一般会有 4 个咖啡师值班,高峰时候可能会增加一些 。订单传给经理后,不等咖啡做出来,而是接着处理下一个订单 。一杯咖啡做完之后,放到出餐流水线(IO Events 队列),送达前台后,跑堂的喊名字,顾客过来取




推荐阅读