## [IO模式](id:)
> **对于一次 IO 访问(以 read 举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。**所以说,当一个 read 操作发生时,它会经历**两个阶段**:
> **1. 等待数据准备** (Waiting for the data to be ready)【以上所讲】
> **2. 将数据从内核的缓冲区拷贝到进程中** (Copying the data from the kernel to the process)
> **linux系统共产生了下面五种网络(IO)模式的方案**
> - 阻塞 I/O(blocking IO)
> - 非阻塞 I/O(nonblocking IO)
> - I/O 多路复用( IO multiplexing)
> - 信号驱动 I/O( signal driven IO)【不常用】
> - 异步 I/O(asynchronous IO)
***
## [阻塞 I/O(blocking IO)](id:)
> 在 Linux 中,默认情况下所有的 socket(连接管道)都是 blocking(阻塞),一个典型的读操作流程大概是这样:
阻塞 I/O 模型
> 当用户进程调用了 recvfrom 这个系统调用,kernel 就开始了 IO 的每一个阶段:
> * 准备数据(对于网络 IO 来说,很多时候数据在一开始还没有到达。比如,还没有收到一个完整的 UDP 包。这个时候 kernel 就要等待足够的数据到来)
这个过程需要等待,也就是说数据被拷贝到操作系统内核的缓冲区中是需要一个过程的。而在用户进程这边,整个进程会被阻塞(当然,是进程自己选择的阻塞)
> * 当 kernel 一直等到数据准备好了,它就会将数据从 kernel 中拷贝到用户内存,然后 kernel 返回结果,用户进程才解除 block 的状态,重新运行起来。
> 所以,**blocking IO 的特点**就是在 IO 执行的**两个阶段都被 block** 了。
***
## [非阻塞 I/O(nonblocking IO)](id:)
> Linux 下,可以通过设置 socket 使其变为 non-blocking。当对一个 non-blocking socket 执行读操作时,流程如下:
非阻塞 I/O 模型
> 当用户进程发出 read 操作时,如果 kernel 中的数据还没有准备好,那么它并不会 block 用户进程,而是立刻返回一个 error。
从用户进程角度讲,它发起一个 read 操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个 error 时,它就知道数据还没有准备好,于是它可以再次发送 read 操作。
一旦 kernel 中的数据准备好了,并且又再次收到了用户进程的 system call(系统调用),那么它马上就将数据拷贝到了用户内存,然后返回。
> 所以,**nonblocking IO 的特点**是用户进程需要**不断的主动询问 kernel 数据好了没有**。
***
## [I/O 多路复用( IO multiplexing)](id:)
> IO multiplexing 就是我们说的 select,poll,epoll,有些地方也称这种 IO 方式为 event driven IO(**事件驱动 IO**)。select/epoll 的好处就在于单个 process 就可以同时处理多个网络连接的 IO。它的基本原理就是 select,poll,epoll 这个 function 会不断的轮询所负责的所有 socket,当某个 socket 有数据到达了,就通知用户进程。
I/O 多路复用模型
> * **当用户进程调用了 select,那么整个进程会被 block**,而同时,kernel 会“监视”所有 select 负责的 socket,当任何一个 socket 中的数据准备好了,select 就会返回。这个时候用户进程再调用 read 操作,将数据从 kernel 拷贝到用户进程。
>
>
> * 所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select() 函数就可以返回。
>
>
> * 这个图和 blocking IO 的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个 system call (select 和 recvfrom),而 blocking IO 只调用了一个 system call (recvfrom)。但是,用 select 的优势在于它可以同时处理多个 connection。
>
>
> * 所以,如果处理的连接数不是很高的话,使用 select/epoll 的 web server 不一定比使用 multi-threading + blocking IO 的 web server 性能更好,可能延迟还更大。**select/epoll 的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。**
>
>
> * 在 IO multiplexing Model 中,实际中,对于每一个 socket,一般都设置成为 non-blocking,但是,如上图所示,整个用户的 process 其实是一直被 block 的。只不过 process 是被 select 这个函数 block,而不是被 socket IO 给 block。
***
## [异步 I/O(asynchronous IO)](id:)
> Linux下的 asynchronous IO 实际应用中很少,流程如下:
异步 I/O 模型
> 用户进程发起 read 操作之后,立刻就可以开始去做其它的事。而另一方面,从 kernel 的角度,当它受到一个 asynchronous read 之后,首先它会立刻返回,所以不会对用户进程产生任何 block。然后,kernel 会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel 会给用户进程发送一个 signal,告诉它 read 操作已完成。
***
## [总结](id:)
> blocking 和 non-blocking 的区别:
调用 blocking IO 会一直 block 住对应的进程直到操作完成,而 non-blocking IO 在 kernel 还准备数据的情况下会立刻返回。
> synchronous IO 和 asynchronous IO 的区别:
在说明 synchronous IO 和 asynchronous IO 的区别之前,需要先给出两者的定义。POSIX`[POSIX 是 IEEE(电子和电器工程师协会) 为各种UNIX操作系统上运行的软件定义的一系列 API 接口标准]` 的定义是这样子的:
> - A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;
> - An asynchronous I/O operation does not cause the requesting process to be blocked;
**两者的区别就在于 synchronous IO 做“IO operation”的时候会将 process 阻塞。按照这个定义,之前所述的 blocking IO,non-blocking IO,IO multiplexing 都属于 synchronous IO。**
> 有人会说,non-blocking IO 并没有被 block 啊。这里有个非常“狡猾”的地方,定义中所指的“IO operation”是指真实的 IO 操作,就是例子中的 recvfrom 这个 system call。non-blocking IO 在执行 recvfrom 这个 system call 的时候,如果 kernel 的数据没有准备好,这时候不会 block 进程。但是,当 kernel 中数据准备好的时候,recvfrom 会将数据从 kernel 拷贝到用户内存中,这个时候进程是被 block 了,在这段时间内,进程是被 block 的。
> 而 asynchronous IO 则不一样,当进程发起 IO 操作之后,就直接返回再也不理睬了,直到 kernel 发送一个信号,告诉进程说 IO 完成。在这整个过程中,进程完全没有被 block。
## 各个 IO Model 的比较如图所示:
五种 I/O 模型比较
> 通过上面的图片,可以发现 non-blocking IO 和 asynchronous IO 的区别还是很明显的。在 non-blocking IO 中,虽然进程大部分时间都不会被 block,但是它仍然要求进程去主动的 check,并且当数据准备完成以后,也需要进程主动的再次调用 recvfrom 来将数据拷贝到用户内存。而 asynchronous IO 则完全不同。它就像是用户进程将整个 IO 操作交给了他人(kernel)完成,然后他人做完后发信号通知。在此期间,用户进程不需要去检查 IO 操作的状态,也不需要主动的去拷贝数据。