IO

67 阅读3分钟

IO模型

IO流程

  • IO分为两个阶段,第一步是等待数据写入缓冲区,第二步将数据从缓冲区拷贝到应用程序,第一步是由对方决定什么时候把数据发过来,第二步是由操作系统决定的,需要从用户态进入内核态拷贝数据;
  • 阻塞和非阻塞指的是第一个阶段,进程等待缓存区写入数据的方式,同步异步指的是第二个阶段,进程是否需要等待操作系统把数据从缓冲区拷贝到应用程序

五种IO模型

  • 同步阻塞:第一个阶段阻塞,直到缓冲区被写入数据
  • 同步非阻塞:采用轮询的方式查看数据是否准备好
  • 多路复用:使用一个线程,监听多个IO请求,并且在有IO请求产生的时候返回,多路复用的阻塞在于应用层面
  • 信号驱动:由信号主动告知数据有没有准备好,线程收到信号后处理IO
  • 异步IO:之前的模型都是在第一个阶段进行优化,第二阶段都需要进入内核态拷贝数据,在异步IO中当程序需要IO时发出IO请求,继续执行后续代码,直到IO完成回调

Java中的IO模型

  • BIO:用户调用read之后会一直阻塞,直到数据拷贝到应用程序
  • NIO:java中的nio可以看作多路复用模型,线程发起select调用,等到内核吧数据准备好,线程再发起read调用
  • AIO:基于事件和回调机制实现,应用程序操作后直接返回,不阻塞,等待操作系统通知

IO多路复用

  • select/poll/epoll是内核提供给用户多路复用的系统调用

select/poll

  • 将已连接到socket都放到一个文件描述集合,调用select函数将文件描述拷贝到内核,让内核检查是否有完了时间发生,检查的方式是遍历,当有事件时,标记此socket为可读或可写,再把文件描述符集合拷贝到用户态,用户态通过遍历的方式找到可操作socket
  • 需要两次遍历和两次文件描述符集合拷贝
  • select采用固定长度bitmap标识文件描述符集合,是有限制的,poll采用动态数组,以链表的形式进行组织,突破个数限制
  • select/poll都是采用线性结构存储socket集合,都需要遍历文件描述符结合来查找,时间复杂度为On,也需要用户态和内核态之间拷贝

epoll

  • epoll在内核使用红黑树来关注进程所有待监测的socket,通过管理红黑树,不需要每次都传入整个socket集合,减少了数据拷贝和内存分配
  • epoll使用事件驱动的机制,内核中维护了一个链表来记录就绪事件,当某个socket有事件发生时,通过回调函数内核会将其加入就绪事件列表,不需要扫描整个socket集合,提高效率
  • epoll支持边缘触发和水平触发两种触发模式,边缘触发是指当有可读事件发生时,只会从wait中苏醒一次,需要保证一次性将内核缓冲区读完;水平触发是值当有可读事件发生时,会不断从wait中苏醒,直到内核缓存区数据被读完;select只支持水平触发,一般来说边缘触发方式比水平触发效率高,因为边缘触发可以减少系统调用次数