一、关于I/O模型的引出
我们都知道,为了OS的安全性等的考虑,进程是无法直接操作I/O设备的,其必须通过系统调用请求内核来协助完成I/O动作,而内核会为每个I/O设备维护一个buffer。如下图所示:
整个请求过程为: 用户进程发起请求,内核接受到请求后,从I/O设备中获取数据到buffer中,再将buffer中的数据copy到用户进程的地址空间,该用户进程获取到数据后再响应客户端。
在整个请求过程中,数据输入至buffer需要时间,而从buffer复制数据至进程也需要时间。因此根据在这两段时间内等待方式的不同,I/O动作可以分为以下五种模式:
(1) 同步阻塞I/O (Blocking I/O)
(2) 同步非阻塞I/O (Non-Blocking I/O)
(3) I/O复用(I/O Multiplexing)
(4) 信号驱动的I/O (Signal Driven I/O)
(5) 异步I/O (Asynchrnous I/O)
二、关于I/O模型的划分
阻塞:调用的进程一直处于等待状态,直到操作完成。
非阻塞:在内核的数据还未准备好时,会立即返回,进程可以去干其他事情。
从同步异步,以及阻塞、非阻塞两个维度来划分来看:
三、
1.同步阻塞I/O
在调用 read
系统调用时,应用程序会阻塞并对内核进行上下文切换。直有得到结果之后才会解除阻塞并返回
2.同步非阻塞I/O
发出调用后,程序不会阻塞,但是需要不断轮询去查看数据是否已经准备好,所以该过程会大量消耗CPU
3.I/O复用
使得我们能同时监听多个I/O事件,将I/O事件添加到select/poll/epoll系统调用上,进程阻塞在select上,而不是read/write系统调用上,当有任意一个I/O事件有数据,select就返回。调用2次系统调用select 和read
4.信号驱动
首先开启套接字的信号驱动式I/O功能,并通过sigaction系统调用安装一个信号处理函数。改系统调用将立即返回,我们的进程继续工作,也就是说他没有被阻塞。当数据报准备好读取时,内核就为该进程产生一个SIGIO信号。我们随后就可以在信号处理函数中调用read读取数据报,并通知主循环数据已经准备好待处理,也可以立即通知主循环,让它读取数据报
优点:不阻塞
缺点:信号队列溢出
5.异步I/O
读请求会立即返回,说明 read
请求已经成功发起了。在后台完成读操作时,应用程序然后会执行其他处理操作。当 read
的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次 I/O 处理过程。
发出一个调用就返回了,没有得到结果,而是被调用者通过信号或者回调函数来处理这个I/O