I/O模式(BIO/NIO/AIO)

发布时间 2023-06-08 16:16:41作者: 上好佳28

I/O过程

对于一次IO访问(这回以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝到应用程序的缓冲区,最后交给进程。所以说,当一个read操作发生时,它会经历两个阶段:

1. 等待数据准备 (Waiting for the data to be ready)(从磁盘拷贝到内核缓冲区)
2. 将数据从内核拷贝到进程中 (Copying the data from the kernel to the process)

block I/O模型(阻塞I/O)

 

read为例:

(1)进程发起read,进行recvfrom系统调用;

(2)内核开始第一阶段,准备数据(从磁盘拷贝到缓冲区),进程请求的数据并不是一下就能准备好;准备数据是要消耗时间的;

(3)与此同时,进程阻塞(进程是自己选择阻塞与否),等待数据ing;

(4)直到数据从内核拷贝到了用户空间,内核返回结果,进程解除阻塞。

也就是说,内核准备数据数据从内核拷贝到进程内存地址这两个过程都是阻塞的。

 

non-block(非阻塞I/O模型)

(1)当用户进程发出read操作时,如果kernel中的数据还没有准备好;

(2)那么它并不会block用户进程,而是立刻返回一个error,从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果;

(3)用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次发送read操作。一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call;

(4)那么它马上就将数据拷贝到了用户内存,然后返回。

所以,nonblocking IO的特点是用户进程内核准备数据的阶段需要不断的主动询问数据好了没有

 

IO多路复用

I/O多路复用实际上就是用select, poll, epoll监听多个io对象,当io对象有变化(有数据)的时候就通知用户进程。好处就是单个进程可以处理多个socket。

(1)当用户进程调用了select,那么整个进程会被block;

(2)而同时,kernel会“监视”所有select负责的socket;

(3)当任何一个socket中的数据准备好了,select就会返回;

(4)这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。

所以,I/O 多路复用的特点是通过一种机制一个进程能同时等待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就可以返回

这个图和blocking IO的图其实并没有太大的不同,事实上,还更差一些。因为这里需要使用两个system call (select 和 recvfrom),而blocking IO只调用了一个system call (recvfrom)。但是,用select的优势在于它可以同时处理多个connection。

select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

 

异步IO

 

(1)用户进程发起read操作之后,立刻就可以开始去做其它的事。

(2)而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。

(3)然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。

总结

BIO(blocking IO):会一直block住对应的进程直到操作完成

NIO(non-blocking IO):在kernel还没准备好数据的情况下会立刻返回,但是,当kernel中数据准备好的时候,recvfrom会将数据从kernel拷贝到用户内存中,这个时候进程是被block了。虽然进程大部分时间都不会被block,但是它仍然要求进程不断的去主动的check,并且当数据准备完成以后,也需要进程主动的再次调用recvfrom来将数据拷贝到用户内存

AIO(asynchronous IO):将整个IO操作交给了它人(kernel)完成,然后做完后发信号通知。在此期间,用户进程不需要去检查IO操作的状态,也不需要主动的去拷贝数据