常见I/O模型

什么是I/O

我们都知道,数据的持久化是发生磁盘的,而能够和硬件打交道的是内核,应用程序想要获得数据就只能通过系统调用获取数据,当进程发生系统调用时,系统进入内核模式,然后开始I/O操作,主要分为两个步骤,1.磁盘把数据装载到内核内存空间 2.内核内存空间拷贝到用户内存空间(I/O发生)

此处输入图片的描述

常见I/O模型

I/O阻塞

进程发起I/O调用,内核拷贝数据期间进程会因为等待数据拷贝会挂起,把cpu时间片让出去,此时叫做I/O阻塞,I/O完成,进程会被唤醒。

此处输入图片的描述

非阻塞I/O

进程发起I/O调用,I/O自己知道需要一段时间才能完成,马上通知进程进行其他操作,叫做非阻塞I/O,在等待期间进程隔一段时间询问是否I/O完成(cpu使用效率不高)。

此处输入图片的描述

I/O复用

同时发生多个系统调用,统一使用一个select进行发起调用,内核内存获取到数据后通知调用方,select发起第二阶段的拷贝,进程只会阻塞在第二阶段。

此处输入图片的描述

事件(信号)驱动I/O

水平触发驱动:内核通知进程读取数据,如果没有读取,不断通知进程 边缘触发驱动:内核只通知一次进程读取数据,进程在超时前可以读取数据 事件驱动方式CPU使用效率高

此处输入图片的描述

异步I/O 进程发起数据请求,内核马上返回信息,进程继续其他操作,内核通知取数据信号,进程处理数据,异步I/O和事件驱动I/O区别在于异步I/O是通知I/O完成

此处输入图片的描述

阻塞式I/O、非阻塞式I/O、I/O复用和信号驱动I/O都是同步I/O,它们的主要区别在第一个阶段。非阻塞式I/O、信号驱动I/O和异步I/O在第一阶段不会阻塞。

##常见web工作模式 prefork模式: 当请求到达时,主进程生成多个工作进程,由工作进程一对一响应请求

work工作模式: 当请求到达时,主进程生成多个线程,每个线程去响应客户端请求

Event工作模式 当请求到达时,主进程生成多个工作进程,每个进程响应多个客户端请求,当执行I/O操作时,会响应其他请求,等内核通知完成I/O时,重新恢复继续请求处理

基于I/O复用模型的的select poll epoll机制

select poll epoll都是I/O多路复用机制,我们都知道在linux系统中所有I/O设备都被看作文件,文件通过一个描述符来进行身份识别。I/O多路复用机制其通过监视多个描述符,一旦描述符就绪,就通知程序进行读写响应,属于同步,阻塞的。

select 借鉴一张网上的关于select基本原理的热图

此处输入图片的描述

select机制缺点:

平均的算,1个进程支持1000并发,百万级并发需要1k的进程,加上数据的内核/用户间拷贝,轮询,很难支持万级以上的级别的并发访问

poll poll使用链表结构保存文件描述符,没有上限的限制,本质上 和select没多大差别,轮询,内核/用户copy开销,随文件描述符增加开销线性增加

epoll epoll支持同时支持水平和边缘触发,边缘触发效率更高,避免了重复事件触发的次数

epoll通过在linux内核中申请一个简易的文件系统,linux通过以下三个过程实现:

BIO NIO AIO

BIO 在java的JDK1.4之前,通过在服务端启动serversocket,客户端启动socket,在两端进行点对点的通信,服务端需要对每个请求建立一堆线程等待请求,当请求到达服务端后,客户端要等待服务端有线程响应或拒绝时,请求结束后才会继续执行接下来的任务。这种模式称为BIO。采用的阻塞模型。

NIO

当链接建立完成后,I/O的数据未必马上到达,在这期间中为了使等待I/O的线程可以继续其他事件,引入缓冲区,再由线程处理I/O

缓冲区和通道的关系,相当于如果想将数据发送的目标端,需要将发送端的buffer写入目标端的channel,然后目标端从chnnel中读取到目标端的buffer

selector运行单线程处理多个channel,当我们获取I/O时,先向selector注册channel,然后调用select方法,这个方法一直轮询I/O事件的就绪,在有就绪时就通知线程处理事件。

通过对selector感兴趣的事件分开为Reactor,每个reactor负责其中一种类型事件,达到分离阻塞级别,减少轮询时间,而且线程直接从set中得到感兴趣的时间

NIO和linux的epoll非常相似,都是基于通道和缓冲区,不同的是,对于就绪的I/O事件,NIO是通过selector轮询,epoll是通过自动发送消息通知select。epoll相对来说更高效

AIO

与NIO不同,AIO机制中,在进行读写操作时,只需直接调用API的read和write方法即可,这两种方法是异步的。对于读操作,当有流流入时,操作系统将可读的流传入read方法缓冲区,并通知应用程序;对于写操作,当操作系统将write方法写入流完毕后,操作系统通知应用程序,read/write方法返回一个带有回调函数的对象。当方法return时直接调用回调函数。

##Reactor和Proactor模式

同步和异步

同步和异步是相对应用程序和内核交互而言,同步至用户进程触发I/O操作后等待或轮询查看I/O操作是否就绪;而异步则是在触发I/O后应用程序开始做其他事情,待I/O操作完成时通知。

阻塞和非阻塞

阻塞和非阻塞针对在进程访问数据时,根据I/O操作就绪状态采取不同方式。阻塞方式读取或写入函数将一直等到,非阻塞方式读取,写入函数立即返回状态值。