3 回答
TA贡献1846条经验 获得超7个赞
看起来所讨论的一些实体(例如:libev等)已经失去了相关性,因为它已经有一段时间了,但我认为这个问题仍然具有很大的潜力。
让我尝试在抽象的UNIX环境中,在Node的上下文中,借助抽象示例来解释事件驱动模型的工作。
计划的观点:
脚本引擎开始执行脚本。
每当遇到CPU绑定操作时,它都会在内联(真实机器)中完成。
每当遇到I / O绑定操作时,请求及其完成处理程序都会在“事件机器”(虚拟机)中注册
以相同的方式重复操作,直到脚本结束。CPU绑定操作 - 执行内联,I / O绑定操作,如上所述向机器请求。
当I / O完成时,将回调一下监听器。
上面的事件机制称为libuv AKA事件循环框架。Node利用此库来实现其事件驱动的编程模型。
Node的观点:
有一个线程来托管运行时。
拿起用户脚本。
将其编译为本机[杠杆v8]
加载二进制文件,然后跳转到入口点。
已编译的代码使用编程原语在线执行CPU绑定活动。
许多I / O和计时器相关的代码都有本机包装。例如,网络I / O.
因此,I / O调用从脚本路由到C ++桥接器,I / O句柄和完成处理程序作为参数传递。
本机代码执行libuv循环。它获取循环,将表示I / O的低级事件排入队列,并将本机回调包装器排入libuv循环结构。
本机代码返回到脚本 - 此刻不会发生I / O!
上面的项目重复多次,直到执行所有非I / O代码,并且所有I / O代码都被注册到libuv。
最后,当系统中没有任何内容要执行时,节点将控制权传递给libuv
libuv开始行动,它获取所有已注册的事件,查询操作系统以获得其可操作性。
在非阻塞模式下准备好I / O的那些被拾取,执行I / O,并且发出它们的回调。一个接一个地。
尚未准备好的那些(例如套接字读取,另一个端点尚未写入任何内容)将继续用OS进行探测,直到它们可用。
循环内部维持一个不断增加的计时器。当应用程序请求延迟回调(例如setTimeout)时,将利用此内部计时器值来计算触发回调的正确时间。
虽然大多数功能都以这种方式迎合,但文件操作的一些(异步版本)是在附加线程的帮助下执行的,并且很好地集成到libuv中。虽然网络I / O操作可以等待期望外部事件,例如另一个端点响应数据等,但文件操作需要来自节点本身的一些工作。例如,如果你打开一个文件并等待fd准备好数据,它就不会发生,因为没有人正在阅读!同时,如果您从主线程中的内联文件中读取,它可能会阻止程序中的其他活动,并且可能会产生可见问题,因为与cpu绑定活动相比,文件操作非常慢。因此,内部工作线程(可通过UV_THREADPOOL_SIZE环境变量配置)用于对文件进行操作,
希望这可以帮助。
TA贡献1873条经验 获得超9个赞
ibuv简介
该Node.js的项目开始于2009年从浏览器分离一个JavaScript环境。使用谷歌的V8和Marc Lehmann的libev,node.js将I / O模型 - 偶数 - 与一种非常适合编程风格的语言相结合; 由于浏览器的形成方式。随着node.js越来越流行,让它在Windows上运行很重要,但libev只能在Unix上运行。Windows等效的内核事件通知机制(如kqueue或(e)轮询)是IOCP。libuv是一个围绕libev或IOCP的抽象,取决于平台,为用户提供基于libev的API。在node-v0.9.0版本的libuv中,libev被删除了。
还有一张用@ BusyRich描述Node.js中的事件循环的图片
根据这个doc Node.js事件循环,
下图显示了事件循环操作顺序的简要概述。
┌───────────────────────┐
┌─>│ timers │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ I/O callbacks │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
│ │ idle, prepare │
│ └──────────┬────────────┘ ┌───────────────┐
│ ┌──────────┴────────────┐ │ incoming: │
│ │ poll │<─────┤ connections, │
│ └──────────┬────────────┘ │ data, etc. │
│ ┌──────────┴────────────┐ └───────────────┘
│ │ check │
│ └──────────┬────────────┘
│ ┌──────────┴────────────┐
└──┤ close callbacks │
└───────────────────────┘
注意:每个框将被称为事件循环的“阶段”。
阶段概述
定时器:此阶段执行由setTimeout()和调度的回调setInterval()。
I / O回调:除了执行几乎所有回调之外
关闭回调,由计时器安排的回调,和setImmediate()。空闲,准备:仅在内部使用。
poll:检索新的I / O事件; 节点将在适当时阻止此处。
check:setImmediate()在这里调用回调。
关闭回调:例如socket.on('close', ...)。
在事件循环的每次运行之间,Node.js检查它是否在等待任何异步I / O或定时器,如果没有,则检查是否干净。
添加回答
举报