IO 模型

阻塞IO模型

当用户进程调用了recvfrom这个系统调用、内核就开始了IO的第一个阶段:准备数据、对于网络IO来说、很多时候数据在一开始还没有到达(比如、还没有收到一个完整的UDP包)、这个时候内核就要等待足够的数据到来、而在用户进程这边、整个进程会被阻塞、当内核一直等到数据准备好了、它就会将数据从内核中拷贝到用户内存、然后返回结果、用户进程才解除阻塞的状态、重新运行起来、几乎所有的程序员第一次接触到的网络编程都是从listen()、send()、recv()等接口开始的、这些接口都是阻塞型的、

 

同一时间只能处理一个用户进程,IO的整个进程阻塞在用户进程,内核一直等待直到数据准备好,他就会将数据从内核拷贝到内存,返回结果给用户进程,此时用户进程解除阻塞状态

特点
(1)
 进程阻塞挂起不消耗CPU资源、及时响应每个操作

(2) 实现难度低、开发应用较容易

(3) 适用并发量小的网络应用开发

(4) 不适用并发量大的应用、因为一个请求IO会阻塞进程、所以、得为每请求分配一个处理进程(线程)以及时响应、系统开销大

非阻塞IO模型

当用户进程发出read操作时、如果内核中的数据还没有准备好、那么它并不会block用户进程、而是立刻返回一个error、从用户进程角度讲、它发起一个read操作后、并不需要等待、而是马上就得到了一个结果、用户进程判断结果是一个error时、它就知道数据还没有准备好、于是它可以再次发送read操作、一旦内核中的数据准备好了、并且又再次收到了用户进程的系统调用、那么它马上就将数据拷贝到了用户内存、然后返回、非阻塞的接口相比于阻塞型接口的显著差异在于、在被调用之后立即返回

 

用户进程不断的主动询问内核数据准备好了么,整个进程不会阻塞在用户进程。

  1. 适用并发量较小、且不需要及时响应的网络应用开发
  2. 实现难度低、开发应用相对阻塞IO模式较难
  3. 进程轮询(重复)调用、消耗CPU的资源

(三)IO复用模型

多个的进程的IO可以注册到一个复用器(select)上、当用户进程调用该select、select会监听所有注册进来的IO、如果select所有监听的IO在内核缓冲区都没有可读数据、select调用进程会被阻塞、而当任一IO在内核缓冲区中有可数据时、select调用就会返回、而后select调用进程可以自己或通知另外的进程(注册进程)来再次发起读取IO、读取内核中准备好的数据、多个进程注册IO后、只有一个select调用进程被阻塞

IO复用相对阻塞和非阻塞更难简单说明、所以额外解释一段、其实IO复用模型和阻塞IO模型并没有太大的不同、事实上、还更差一些、因为这里需要使用两个系统调用(select和 recvfrom)、而阻塞IO模型只有一次系统调用(recvfrom)、但是、用select的优势在于它可以同时处理多个连接、所以如果处理的连接数不是很高的话、使用select/epoll的web server不一定比使用多线程加阻塞IO的web server性能更好、可能延迟还更大、select/epoll的优势并不是对于单个连接能处理得更快、而是在于能处理更多的连接

  1. 专一进程解决多个进程IO的阻塞问题、性能好、Reactor模式
  2. 实现、开发应用难度较大
  3. 适用高并发服务应用开发、一个进程/线程响应多个请求

(四)信号驱动式IO模型

信号驱动式IO就是指进程预先告知内核、向内核注册一个信号处理函数、然后用户进程返回不阻塞、当内核数据就绪时会发送一个信号给进程、用户进程便在信号处理函数中调用IO读取数据、从图中明白实际IO内核拷贝到用户进程的过程还是阻塞的、信号驱动式IO并没有实现真正的异步、因为通知到进程之后、依然是由进程来完成IO操作、

  1. 在信号驱动式IO模型中、依然不符合POSIX描述的异步IO、只能算是半异步、并且实际中并不常用、
  2. 回调机制、实现、开发应用难度大

(五)异步IO模型

用户进程发起aio_read(POSIX异步IO函数aio_或者lio_开头)操作之后、给内核传递描述符、缓冲区指针、缓冲区大小和read相同的三个参数以及文件偏移(与lseek类似)、告诉内核当整个操作完成时、如何通知我们、立刻就可以开始去做其它的事、而另一方面、从内核的角度、当它受到一个aio_read之后、首先它会立刻返回、所以不会对用户进程产生任何阻塞、然后、内核会等待数据准备完成、然后将数据拷贝到用户内存、当这一切都完成之后、内核会给用户进程发送一个信号、告诉它aio_read操作完成了

工作机制

异步IO的工作机制是:告知内核启动某个操作、并让内核在整个操作完成后通知我们、这种模型与信号驱动的IO区别在于、信号驱动IO是由内核通知我们何时可以启动一个IO操作、这个IO操作由用户自定义的信号函数来实现、而异步IO模型是由内核告知我们IO操作何时完成、

  1. 不阻塞、数据一步到位、Proactor模式
  2. 需要操作系统的底层支持、LINUX 2.5 版本内核首现、2.6 版本产品的内核标准特性
  3. 回调机制、实现、开发应用难度大
  4. 非常适合高性能高并发应用

 

(六)五种IO模型的比较

(1)阻塞IO和非阻塞IO的区别在哪?

前面的介绍中其实已经很明确的说明了这两者的区别、调用阻塞会一直阻塞住对应的进程直到操作完成、而非阻塞IO在内核还没准备数据的情况下会立刻返回、阻塞和非阻塞关注的是进程在等待调用结果时的状态、阻塞是指调用结果返回之前、当前进程会被挂起、调用进程只有在得到结果才会返回、非阻塞调用指不能立刻得到结果、该调用不会阻塞当前进程、

(2)同步IO和异步IO区别在哪?

两者的区别就在于同步做IO操作的时候会将进程阻塞、按照这个定义、之前所述的阻塞IO、非阻塞IO、IO复用、信号驱动都属于同步IO、有人可能会说、非阻塞IO并没有被阻塞啊、这里有个非常狡猾的地方、定义中所指的IO操作是指真实的IO操作、就是例子中的recvfrom这个系统调用、非阻塞IO在执行recvfrom这个系统调用的时候、如果内核的数据没有准备好、这时候不会阻塞进程、但是、当内核中数据准备好的时候、recvfrom会将数据从内核拷贝到用户内存中、这个时候进程是被阻塞了、信号驱动也是同样的道理、在这段时间内、进程是被阻塞的、而异步IO则不一样、当进程发起IO操作之后、就直接返回再也不理睬了、直到内核发送一个信号、告诉进程说IO完成、在这整个过程中、进程完全没有被阻塞、

(3)信号驱动式IO和异步IO的区别?

这里之所以单独拿出来是因为如果还没有清除IO概念很容易混淆、所以理解IO模型之前一定要理解IO概念、如果看完前面两个问题、相信也能理解信号驱动IO与异步IO的区别在于启用异步IO意味着通知内核启动某个IO操作、并让内核在整个操作(包括数据从内核复制到用户缓冲区)完成时通知我们、也就是说、异步IO是由内核通知我们IO操作何时完成、即实际的IO操作也是异步的、信号驱动IO是由内核通知我们何时可以启动一个IO操作、这个IO操作由用户自定义的信号函数来实现

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值