Unix五种IO模型
IO 是主存和外部设备 ( 硬盘、终端和网络等 ) 拷贝数据的过程。 IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成。
1、阻塞式I/O:blocking IO
在linux中,默认情况下所有的socket都是blocking,一个典型的读操作流程大概是这样:
step1: 等待数据到达(例如等待网络数据传输完成)。当所有等待数据到达时,它被复制到内核中的某个缓冲区。
step2: 把数据从内核缓冲区复制到应用程序缓冲区。
[图片上传失败...(image-ac81d1-1550418665511)]
应用进程(application process) 调用了recvfrom这个系统调用
然后内核(kernel)开始了IO的第一个阶段:准备数据。在内核准备数据期间,应用进程是一直阻塞的。
内核准备好数据之后,把数据从内核copy到应用内存。在内核copy数据期间,应用进程依然是一直阻塞的。
内核返回结果,应用进程才解除block的状态,重新继续执行
blocking IO的特点就是在IO执行的两个阶段都被block了。
2、非阻塞式I/O: nonblocking IO
linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking socket执行读操作时,流程是这个样子:
[图片上传失败...(image-b67de6-1550418665511)]
应用进程(application process) 调用了recvfrom这个系统调用
内核告诉应用进程,数据还没有准备好
应用进程发现数据没有准备好,不停的调用recvfrom这个系统调用,内核在数据没有准备好的情况下不停的告诉应用进程数据没有准备好。
内核准备数据。这个过程中,应用进程没有阻塞,因为它在不停的调用!
在应用进程不停的调用中,恰好这次内核把数据准备好了,然后内核把数据copy到应用进程内存。在内核copy数据期间,应用进程是一直阻塞的。
内核返回结果,应用进程解除block的状态,重新继续执行
当用户进程发出read操作时,如果kernel中的数据还没有准备好,那么它并不会block用户进程,而是立刻返回一个error。
从用户进程角度讲 ,它发起一个read操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个error时,它就知道数据还没有准备好,于是它可以再次 发送read操作。
一旦kernel中的数据准备好了,并且又再次收到了用户进程的system call,那么它马上就将数据拷贝到了用户内存,然后返回。
用户进程第一个阶段不是阻塞的,需要不断的主动询问kernel数据好了没有;第二个阶段依然总是阻塞的。
3、I/O复用(select,poll,epoll...):IO multiplexing
IO复用同非阻塞IO本质一样,不过利用了新的select系统调用,由内核来负责本来是请求进程该做的轮询操作。看似比非阻塞IO还多了一个系统调用开销,不过因为可以支持多路IO,才算提高了效率。
它的基本原理就是select /epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。它的流程如图:
[图片上传失败...(image-68441e-1550418665511)]
应用进程 调用了select这个系统调用
内核开始准备数据。在内核准备数据期间,应用进程是一直阻塞的
内核告诉应用进程数据准备好了
应用进程(application process) 调用了recvfrom这个系统调用
内核把数据copy到应用进程内存。在内核copy数据期间,应用进程是一直阻塞的
内核返回结果,应用进程解除block的状态,重新继续执行
如上图所示,整个用户的process其实是一直被 block的。只不过process是被select这个函数block,而不是被socket IO给block。
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个 socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
用select的优势在于它可以同时处理多个connection。
select/epoll的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。 事实上,如果处理的连接数不是很高的话,使用 select/epoll的web server不一定比使用multi-threading + blocking IO的web server性能更好,可能延迟还更大。
4、信号驱动式I/O(SIGIO):signal driven IO
信号驱动式IO是指让操作系统内核在文件描述符就绪时发送信号给用户进程,这样一来用户进程只要调用sigaction后就能立即返回,不会被阻塞。当操作系统的文件描述符就绪时,会发送信号给用户进程,用户进程再调用recvfrom开始IO操作。
[图片上传失败...(image-f90b02-1550418665511)]
应用进程发起一个信号,告诉内核说我需要什么文件,然后直接返回
内核准备数据。应用进程继续完成后面的逻辑,不会阻塞
内核准备好数据,告诉应用进程来取数据
应用进程请求取数据
内核把数据复制到应用进程。应用进程阻塞
返回数据
信号驱动式IO模型跟IO复用模型的区别,前者操作系统会主动发消息给用户进程,后者用户进程还是会阻塞在多路复用器上。
5、异步I/O(POSIX的aio_系列函数):asynchronous IO
异步IO模型指的是告诉操作系统内核启动某个操作,并让操作系统内核在完成整个操作后通知用户进程。
其与信号驱动式IO的区别是信号驱动式IO模型是操作系统内核通知我们何时开始一个IO操作,而异步IO模型是操作系统内核通知我们该IO操作何时完成。
[图片上传失败...(image-b61c9-1550418665511)]
应用进程发起一个信号,告诉内核说我需要什么文件,然后直接返回
内核准备数据。应用进程继续完成后面的逻辑,不会阻塞
内核把数据复制到应用进程内存。应用进程不会阻塞
返回数据
用户进程发起read操作之后,立刻就可以开始去做其它的事。
另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都 完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
在这整个过程中,进程完全没有被block。
总结
blocking I/O
nonblocking I/O
I/O multiplexing
signal driven I/O (SIGIO)
asynchronous I/O
数据准备
阻塞
不阻塞
不阻塞
不阻塞
不阻塞
数据复制
阻塞
阻塞
阻塞
阻塞
不阻塞