# IO模型

## 常见的模型
I/O输入操作的两个阶段  
- 等待数据准备好  
- 从内核向进程复制数据  
根据在这两个不同阶段处理的不同，可以将I/O模型划分为以下五种类型：  
阻塞，非阻塞，IO多路复用，信号驱动式I/O，异步

## 概念说明
1. 用户空间与内核空间  
现在操作系统都是采用虚拟存储器，那么对32位操作系统而言，它的寻址空间（虚拟存储空间）为4G（2的32次方）。操作系统的核心是内核，独立于普通的应用程序，可以访问受保护的内存空间，也有访问底层硬件设备的所有权限。为了保证用户进程不能直接操作内核（kernel），保证内核的安全，操心系统将虚拟空间划分为两部分，一部分为内核空间，一部分为用户空间。  
![这里写图片描述](https://img-blog.csdn.net/20180613143011968?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1RpbWU4ODg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

2. 进程切换  
为了控制进程的执行，内核必须有能力挂起正在CPU上运行的进程，并恢复以前挂起的某个进程的执行。这种行为被称为进程切换。因此可以说，任何进程都是在操作系统内核的支持下运行的，是与内核紧密相关的。

3. 进程的阻塞  
正在执行的进程，由于期待的某些事件未发生，如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作做等，则由系统自动执行阻塞原语(Block)，使自己由运行状态变为阻塞状态。可见，进程的阻塞是进程自身的一种主动行为，也因此只有处于运行态的进程（获得CPU），才可能将其转为阻塞状态。**当进程进入阻塞状态，是不占用CPU资源的。**

4. 文件描述符fd  
文件描述符（File descriptor）是计算机科学中的一个术语，是一个用于表述指向文件的引用的抽象化概念。
文件描述符在形式上是一个非负整数。实际上，它是一个索引值，指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时，内核向进程返回一个文件描述符。在程序设计中，一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

5. 缓存 I/O
缓存 I/O 又被称作标准 I/O，大多数文件系统的默认 I/O 操作都是缓存 I/O。在 Linux 的缓存 I/O 机制中，操作系统会将 I/O 的数据缓存在文件系统的页缓存（ page cache ）中，也就是说，数据会先被拷贝到操作系统内核的缓冲区中，然后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。
**缺点：数据在传输过程中需要在应用程序地址空间和内核进行多次数据拷贝操作，这些数据拷贝操作所带来的 CPU 以及内存开销是非常大的。**

## I/O模型

#### 阻塞
![](http://img.blog.csdn.net/20160609135646100)
1. 举例  
我和女友点完餐后，不知道什么时候能做好，只好坐在餐厅里面等，直到做好，然后吃完才离开。
女友本想还和我一起逛街的，但是不知道饭能什么时候做好，只好和我一起在餐厅等，而不能去逛街，直到吃完饭才能去逛街，中间等待做饭的时间浪费掉了。这就是典型的阻塞。

#### 非阻塞
![](http://img.blog.csdn.net/20160609135720523)
1. 举例   
我女友不甘心白白在这等，又想去逛商场，又担心饭好了。所以我们逛一会，回来询问服务员饭好了没有，来来回回好多次，饭都还没吃都快累死了啦。这就是非阻塞。需要不断的询问，是否准备好了。

#### IO多路复用
![](http://img.blog.csdn.net/20160609135743772)

1. 定义
多个描述符的I/O操作都能在一个线程内并发交替地顺序完成，这就叫I/O多路复用，这里的“复用”指的是复用同一个线程。
2. 举例  
餐厅安装了电子屏幕用来显示点餐的状态，这样我和女友逛街一会，回来就不用去询问服务员了，直接看电子屏幕就可以了。这样每个人的餐是否好了，都直接看电子屏幕就可以了，这就是典型的IO多路复用，如select、poll、epoll。（与阻塞模型相比，优势在于可以等待多个描述符）

#### 信号驱动 I/O
![](http://img.blog.csdn.net/20160609135826601)

#### 异步 I/O
![](http://img.blog.csdn.net/20160609135848507)
信号驱动式I/O在数据报准备好时就通知应用进程，应用进程还需要将数据报从内核复制到用户进程缓冲区；而异步I/O模型则是整个操作完成才通知应用进程，应用进程在整个操作期间都不会被阻塞。

#### 比较
![各种I/O模型的比较](http://img.blog.csdn.net/20161021075133062)

#### 举例  
有A，B，C，D，E五个人在钓鱼。 
A使用了最古老的鱼竿，所以开始钓鱼后，就一直守着，直接鱼上钩了再拉竿； 
B由于着急想知道有没鱼上钩，所以隔一会就看一次鱼竿看有没鱼上钩，直到看到鱼上钩后，再拉竿； 
C同时使用了N支鱼竿来钩鱼，然后等着，只要有其中一支鱼竿有鱼上钩，就将对应的鱼竿拉起来； 
D的鱼竿比较高级，当有鱼上钩后，会发出警报提示，所以D开始钓鱼后不用一直守着，一旦鱼竿发出警报，D再回来拉竿即可； 
E为了更省事，直接雇个佣人给他钓鱼，当佣人钓起鱼后，再通知E去取鱼即可。


## 参考资料
* [通俗讲解 异步，非阻塞和 IO 复用](https://www.zybuluo.com/phper/note/595507)
* [I/O model](http://www.masterraghu.com/subjects/np/introduction/unix_network_programming_v1.3/ch06lev1sec2.html)
* [Linux IO模式及 select、poll、epoll详解](https://segmentfault.com/a/1190000003063859)
* [IO多路复用到底是不是异步的？](https://www.zhihu.com/question/59975081/answer/171729721)
* [图解UNIX的I/O模型](http://blog.csdn.net/lihao21/article/details/51620374)