阻塞、非阻塞IO,同步、异步IO
今天闲暇看 Node.js,看到这些概念,有些困惑的地方,Google 一番,做些笔记。 因为术业方向问题,可能理解有误,但记录下来便于日后复习的时候比对。
阻塞 IO (Blocking IO), 非阻塞 IO (Non-Blocking IO)
核心区别在于,发起调用的进程(或线程)是否会挂起等待 IO 完成。
下面举例说明用户进程发起 一个 read 数据的阻塞 IO 操作的过程。 进程在发起系统调用后,就会一直干等。
- 首先等待 kernel 准备数据(如等待完整的网络数据包),这个阶段是 Blocking 的。
- 数据准备完毕后,kernel 将数据从缓冲区拷贝到用户空间,这个阶段也是 Blocking 的。
- 调用返回,用户进程也就解除了阻塞状态。
由上述过程可见,阻塞 IO 操作的流程很简单易懂,但也可以遇见,如果 IO 操作非常费时,那么用户进程在这个过程将会无法响应其他操作,这对某些应用来说,是无法接受的。
那么,一个非阻塞的 IO 又是怎么样的呢?
- 一个read的非阻塞IO,不会挂起用户进程,在 kernel 准备数据中的时候,收到系统调用后,会立即返回一个错误状态,用户进程不会被挂起,是Non-Blocking的。
- 发起调用的进程因为马上就能获得数据的准备的状态,因此可以周期性继续发起调用,直至 kernel 准备好数据。
- 当 kernel 准备好数据后,再接收到读取的调用的话,就会将数据拷贝到用户空间,这个阶段是Blocking的。
- 调用返回,圆满结束。
由上述过程可见,非阻塞 IO 操作的流程相比复杂一些,但因为用户进程不会被挂起,因此有机会在 IO 过程做其他事情,灵活很多。
同步 IO
通常情况下, 不论阻塞 IO 还是非阻塞 IO, 均是同步 IO。
异步 IO
着重说明下异步 IO, 因为这是我们 JavaScript 开发者接触最多的一个概念。
一个异步的 IO,发起调用户后会立即返回,可以不必等待 IO 结果去做其他事情,当有结果之后,会收到一个信号通知,从而可以回头进入数据处理环节。
具体的流程如下:
- 发起异步 IO,立即返回,可以做其他事情
- kernel 会默默地准备数据、拷贝数据到内存用户空间,向用户进程发一个 signal 通知其IO已完成
- 调用发起者处理 IO 结果
由此可见,一个异步的 IO 全程不阻塞,也不必关心 IO 的过程状态,省心省事。