Node.js中的IO

luoyjx · 2017-04-28 22:07 · 12次阅读

I/O

阻塞IO

调用之后需要等待系统内核层面完成所有操作后,调用才结束。 缺点: CPU等待IO。

非阻塞IO

调用之后立刻返回,不等待所有操作完成。 缺点: 轮询操作状态。

轮询模型的演进 1.read 周期性的检查IO是否完成。

2.select轮询 采用1024长度的数组存储状态,最多同时检查1024个文件的描述符。

3.poll 采用链表的形式避免长度限制。不能避免不需要的检查。

4.epoll 进入轮询的时候没有检查到 I/O 事件则进行休眠,直到有事件将它唤醒。 利用了事件通知、执行回调的方式。

5.kqueue 类似 epoll ,存在于 FreeBSD。

异步IO

linux libev作者Marc Alexander Lehmann -> libeio。 线程池 + 阻塞I/O -> 异步I/O

windows IOCP 调用异步方法,等待IO完成后的通知,执行回调。(内部线程池实现)

libuv 对不同平台进行了封装,linux下采用自定义的线程池,windows下采用IOCP。

Node是单线程的? JavaScript执行在单线程中而已,I/O任务任然是由线程池来完成。

异步 I/O 组成部分:事件循环观察者请求对象

事件循环

每执行一次循环体成为一个Tick. Tick为了查看是否有事件待处理.有,取出事件及其相关的回调函数。如果存在关联的回调函数,就执行它们。进入下一个循环。如果不在有事件处理,就退出进程。

观察者

用于收集待处理的事件,等待事件循环时,查询是否有待处理事件。文件 IO 观察者、网络 IO 观察者,观察者将事件进行分类。

请求对象

从JavaScript发起调用到内核执行完IO操作的过渡过程中,存在的中间产物,就是请求对象。

fs.open()

node核心库fs调用fs.open() | native库open方法 | libuv判断平台后执行 uv_fs_open | 生成请求对象 FSReqWrap | windows下由 QueueUserWorkItem() 推入线程池 | JavaScript调用立即返回 | 执行后续操作

执行回调

线程池中的IO操作完成后 | 获取结果存储在req -> result属性上 | 调用 PostQueueCompletionStatus() 通知 IOCP,告知当前已完成,释放线程 | Tick执行中调用IOCP的 GetQueudCompletionStatus() 检查是否有执行完的请求,有则请求对象加入到 I/O 观察者的队列中去,然后将其当做事件处理。 | 取出请求对象 result 属性作为参数,取出 oncomplete_sym 属性作为方法,执行回调。

非 I/O 异步

setTimeoutsetIntervalsetImmediateprocess.nextTick

setTimeout、setInterval (非精确)

他们创建的定时器会被插入到定时器观察者内部的一个红黑树中,每次Tick执行时,会从该红黑树中迭代取出定时器对象,检查是否超过定时时间,如果超过,就形成了一个事件,它的回调函数将会立即执行。

process.nextTick

每次执行时只是放入 nextTickQueue,下一次tick时,取出执行。时间复杂度为O(1),红黑树为O(lg(n))。

setImmediate

比process.nextTick晚执行,是由于观察者的顺序,process.nextTick不属于事件循环中了,setImmediate属于check观察者(),晚于它。

1.process.nextTick: 它会立即执行,比如 Timers有5个callback,当执行到第3个时,nextTickQueue中push进了几个callback,那么当第3个timer执行完时,立刻去同步的执行完所有 nextTickQueue中的callback,然后再进行第4个timer的执行.

2.setImmediate: callback保存在一个链表中,每次事件循环中只会执行一个回调函数。 untitled1.png

图来源:http://voidcanvas.com/setimmediate-vs-nexttick-vs-settimeout/

收藏

暂无评论

登录后可以进行评论。没有账号?马上注册