IO复习-IO模型

243 阅读5分钟

IO 模型

转载自:知乎:勤劳的小手 zhuanlan.zhihu.com/p/115912936

阻塞IO

描述:应用A调用recvfrom读取数据时,在数据被复制到应用缓冲区或发送错误时才返回,在此期间一直等待,进程从调用到返回这段时间内都被阻塞。

流程

  1. 应用进程向内核发起recfrom读取数据
  2. 准备数据报(应用进程阻塞)
  3. 将数据从内核复制到应用空间
  4. 复制完成后,返回成功提示。

优点:开发简单,等待数据过程中,用户线程挂起,不占用CPU资源。

缺点:阻塞全截断,不灵活,不支持高并发。

非阻塞IO

描述:在应用调用recvfrom读取数据时,如果没有数据,就会直接返回一个ewouldblock错误,不会阻塞在等待数据的第二阶段。同时,意味着应用要读取数据就需要不断的调用recvfrom请求,直到读取到数据为止。

流程

  1. 应用进程向内核发起recvfrom读取数据
  2. 没有数据包准备好,即刻返回EWOULDBLOCK错误码
  3. 应用进程向内核发起recvfrom读取数据...
  4. 若已有数据包,则执行5
  5. 将内核中的数据拷贝到用户空间
  6. 完成后,返回成功提示。

优:每次发起recvfrom调用,立即返回不会阻塞,实时性比较好。 缺:不断轮询内核,占用大量的CPU资源,效率低下。

IO复用模型

存在问题:并发环境下,若发送给B多个消息,就需要创建多个线程去调用 recvfrom读取数据。若为同步操作,若连接请求上百万,那么久需要上百万的线程去不断向内核发送recvfrom请求,读取数据,这样太浪费资源了。

衍生解决:linux系统,引入select/poll系统,用一个线程(函数)监控多个网络请求,linux称为fd文件扫描符(fd:标识一个网络请求),只需要一个或几个线程完成数据状态的询问操作,当数据准备就绪后再分配对应带的线程去读取数据。常:select/poll帮我看看,有数据的话通知我,我拿

这个函数:select/poll/epoll函数,线程利用select函数监控多个fd,监控的fd中只要有任何一个数据状态准备就绪,select函数就返回可读状态,这时询问线程再去通知处理数据的线程,此时线程再发起recvfrom请求。 总结:复用IO的基本思路就是通过slect或poll、epoll 来监控多fd ,来达到不必为每个fd创建一个对应的监控线程,从而减少线程资源创建的目的。

优点:selector线程可以同时处理上万个链接fd,不用创建那么多线程,减少开销。 缺点:select/poll调用也属于阻塞式,因此整个数据获取阶段也是阻塞式的,唯一不用的是一次性管理N多个链接fd。

select/poll/epoll对比

  1. select/poll默认单线程最多打开1024个fd。而epoll能配置
  2. select/poll采用不断去轮询就绪的fd,一旦fd太多,轮询效率高,导致IO效率降低,而epoll采用热点探测:实现一个伪AIO,让就绪的fd回调,只会对就绪的socket进行操作。
  3. epoll使用mmap来避免不必要的内存复制
  4. epoll的API更简单易用

信号驱动IO模型

问题:虽然复用IO模型解决了线程问题,但select采用轮询的方式监控fd,有点暴力,因为大多数轮询无效。为什么不能让我通知你数据好了呢?这就衍生了信号驱动IO模型。

不用轮询监控,而是在调用sigaction时候建立sigio的信号联系,当内核数据准备好之后再通经过SIGIO信号通知线程数据准备好的状态。线程收到信号后再向内核发起recvfrom读取请求。因为信号驱动IO模型下应用多线程在发出信号监控后即可返回,不会阻塞。

总结: IO复用模型里面的select虽然可以监控多个fd了,但select其实现的本质上还是通过不断的轮询fd来监控数据状态, 因为大部分轮询请求其实都是无效的,所以信号驱动IO意在通过这种建立信号关联的方式,实现了发出请求后只需要等待数据就绪的通知即可,这样就可以避免大量无效的数据状态轮询操作

异步IO

问题:之前无论IO复用或信号驱动IO,都需要先select询问状态,再 recvfrom获取数据。能不能直接通知内核我要数据,剩下的交给你? 解决:应用告知内核启动某个操作,让内核在整个操作完成后,通知应用,这种模型与信号驱动模型的主要区别是,信号驱动IO是内核告知可开始下一个IO操作,而异步IO模型是由内核告知操作完成。

总结:异步IO的思路是解决发送询问和,发送接收数据请求的模式,现在只用发送一次请求就完成状态询问和拷贝数据操作。

优点:全阶段异步,用户进程只需要接收内核的操作完成事件或注册一个IO回调函数。

缺点:依赖OS底层实现,而目前linux下的AIO模型不完善,因此用到最多的还是多路复用IO模型

再谈IO模型里的同步和异步

阻塞:读取数据时,数据还没准备就绪的时候,如果是需要等待,则为阻塞。

非阻塞:上述情况,直接返回请求。

同步:发送请求到数据最后复制完成都需要参与的称为同步请求。

异步:发送完请求,不参与过程,只等最后通知,称为异步。

同步阻塞 同步非阻塞 异步非阻塞 为什么没有异步阻塞?因为异步模型下,发送请求后就即可返回了,所以不会阻塞。

谈谈你认识的NIO

  1. 上诉的NIO模型非阻塞IO
  2. BEW IO包:对多路复用模型再次封装后提供的代码层的API

组件:selector、channel、buffer