异步IO
异步I/O的原因:
(1)提升用户体验:同步耗时M+N,异步为max(M, N)
(2)资源分配:利用单线程,远离多线程死锁、状态同步等问题;利用异步I/O,让单线程远离阻塞,以更好地使用CPU。为了弥补单线程无法利用多核CPU的缺陷,Node提供了类似前端浏览器中Web Workers的子进程,子进程可以通过工作进程高效地利用CPU和I/O。
异步I/O与非阻塞I/O
操作系统内核对于I/O只有两种方式:阻塞与非阻塞(在调用阻塞I/O时,应用程序需要等待I/O完成才返回结果)
轮询:非阻塞I/O未完成任务之前,返回的是当前调用的状态。为了获取完整的数据,应用程序需要重复调用I/O操作来确认是否完成。这种重复调用判断操作是否完成的技术叫做轮询。对于应用程序而言,它仍是一种同步,因为应用程序仍然需要花费大量时间来等待I/O完全返回,等待期间,CPU要么用于遍历文件描述符的状态,要么用于休眠等待事件发生。
阻塞I/O造成CPU等待浪费,非阻塞的轮询会让CPU处理状态判断,是对CPU资源的浪费,CPU要么用于遍历文件描述符的状态,要么用于休眠等待事件发生。
主要的轮询技术:
- read:最原始、性能最低 通过重复调用来检查I/O的状态来完成完整数据的读取。在得到最终数据前,CPU一直耗用在等待上
- select:通过对文件描述符上的事件状态来进行判断
select轮询具有一个较弱的限制:它采用一个1024长度的数组来存储状态,所以最多可以同时检查1024个文件描述符。
- poll:采用链表的方式避免数组长度的限制和不必要的检查
- epoll:效率最高的I/O事件通知机制 在进入轮询的时候如果没有检查到I/O事件,将会进行休眠,直到事件发生将它唤醒。真实利用了事件通知、执行回调的方式,而不是遍历查询,所以不会浪费CPU,执行效率较高。