另一篇讲得更好的
文章,里面详细讲述了阻塞 非阻塞 同步 异步这几个
阻塞I/O
非阻塞I/O
I/O复用(select和poll)
信号驱动I/O(SIGIO)
异步I/O(POSIX的aio_系列函数)
如下全部例子都使用recvfrom()函数来作讲解。
阻塞I/O模型
在缺省状况下,全部套接口都是阻塞的,进程调用recvfrom,其系统调用直到数据报到达且被拷贝到应用进程的缓冲区或者发生错误才返回。最多见的错误是系统调用被信号中断。咱们说进程从调用recvfrom开始到它返回的整段时间内是被阻塞的,recvfrom成功返回后,进程开始处理数据报。
应用进程 内核
| recvfrom =========系统调用========> 无数据报准备好 ||
| || 等待
| || 数据
进程阻塞于 《 数据报准备好 ||
recvfrom的调用 | 拷贝数据报 ||
| || 将数据从内核
| || 拷贝到用户空间
| 处理数据报 <========返回成功指示=======拷贝完成 ||
非阻塞I/O模型
进程把一个套接口设置成非阻塞是在通知内核:当所请求的I/O操做非得把本进程投入睡眠才能完成时,不要把本进程投入睡眠,而是返回一个错误。
当一个应用程序对一个非阻塞的描述字循环调用recvfrom是,咱们称之为轮询(polling)。应用进程持续轮询内核,以查看某个操做是否就绪。这么作每每耗费大量CPU时间,不过这种模型不多会用到。
应用进程 内核
| recvfrom =========系统调用========> 无数据报准备好 |
| <====EWOULDBLOCK====== |
| recvfrom =========系统调用========> 无数据报准备好 |
等
| <====EWOULDBLOCK====== 》
| recvfrom =========系统调用========> 无数据报准备好 |
待
进程反复调用
| <====EWOULDBLOCK====== |
数
recvfrom 等待 《
recvfrom =========系统调用========> 数据报准备好 |
据
返回成功 | 拷贝数据报 T
指示(轮询) | || |
| ||
将数据从内核
| ||
拷贝到用户空间
| || |
| 处理数据报 <========返回成功指示=========拷贝完成 |
I/O复用模型
有了I/O复用(I/O multiplexing),咱们就能够调用select或poll,阻塞在这两个系统调用中的某一个之上,而不是阻塞在真正的I/O系统调用上。
咱们阻塞于select调用,等待数据报套接口变为可读。当select返回套接口可读这一条件时,咱们调用recvfrom把所读数据报拷贝到应用进程缓冲区。
应用进程 内核
|| select =========系统调用========> 无数据报准备好 ||
进程受阻于select
| || |
调用,等待可能
| ||
多个套接口中的 《 》 等待数据
任一个变为可读
| ||
| || |
|| <=======返回可读条件===== || ||
|| recvfrom =========系统调用=======> 数据报准备好 ||
| 拷贝数据报 |
数据拷贝到应用
| || |
缓冲区期间进程
《
》
将数据从内核
阻塞
| ||
拷贝到用户空间
| || |
|| 处理数据报 <========返回成功指示========拷贝完成 ||
信号驱动I/O模型
咱们也能够用信号,让内核在描述字就绪时发送SIGIO信号通知咱们。咱们称这种模型为信号驱动I/O(signal-driven I/O)。
咱们首先开启套接口的信号驱动I/O功能,并经过sigaction系统调用安装一个信号处理函数。该系统调用当即发回,咱们的进程继续工做,也就是说它没有被阻塞。当数据报准备好时,内核就为该进程产生一个SIGIO信号。咱们随后既能够在信号处理函数中调用recvfrom读取数据报,并通知主循环数据已经准备好待处理,也能够当即通知主循环,让它读取数据报。
不管如何处理SIGIO信号,这种模型的优点在于等待数据报到达期间,进程不被阻塞。主循环能够继续执行,只要不时等待来自信号处理函数的通知:既能够是数据已经准备好被处理,也能够是数据报已准备好被读取。
应用进程 内核
创建SIGIO的 =========系统调用========> |
|| 信号处理程序 <==========返回========== |
| |
|
进程继续执行 《 等待数据
| |
| |
||信号信号处理程序 <======递交SIGIO====
数据报准备好
||
|| recvfrom ========系统调用======>
拷贝数据报
||
| || |
数据拷贝到应用
| || |
缓冲区期间进程
《
》
将数据从内核
阻塞
| ||
拷贝到用户空间
| || |
|| 处理数据报 <========返回成功指示========拷贝完成 ||
异步I/O模型
异步I/O(asynchronous I/O)有POSIX规范定义。后来演变成当前POSIX规范的各类早期标准定义的实时函数中存在的差别已经取得一致。通常地说,这些函数的工做机制是:告知内核启动某个操做,并让内核在整个操做(包括将数据从内核拷贝到咱们本身的缓冲区)完成后通知咱们。这种模型与前与前面介绍的信号驱动模型的主要区别在于:信号驱动I/O是由内核通知咱们什么时候能够启动一个I/O操做,而异步I/O模型是由内核通知咱们I/O操做什么时候完成。
应用进程 内核
| aio_read =========系统调用========> 无数据报准备好 ||
| <=========返回========== ||
| 》 等待
数据
| ||
进程继续执行 《 数据报准备好 ||
| 拷贝数据报 ||
| || 将数据从内核
| || 拷贝到用户空间
| 处理数据报 <===递交在aio_read中指定的信号===拷贝完成 ||
咱们调用aio_read函数(POSIX异步I/O函数以aio_或lio_开头),给内核传递描述字、缓冲区指针、缓冲区大小(与read相同的三个参数)、文件偏移(与lseek相似),并告诉内核当整个操做完成时如何通知咱们。该系统调用当即返回,在等待I/O完成期间,咱们的进程不被阻塞。
各类I/O模型比较
==========================================================
|| 阻塞I/O || 非阻塞I/O || I/O复用 || 信号驱动I/O || 异步I/O ||
========================================================== +
|| 发起 || 检查 || 检查 || || 发起 || +
|| | || 检查 || | || || || +
|| | || 检查 || |
阻 || || || +
|| | || 检查 || |
塞 || || || 等待
|| | || 检查 || |
|| || || 数据
|| | || 检查 || |
|| || || +
|| | || 检查 || +
|| || || +
|| | 阻 || 检查 || 就绪发起
|| 通知发起 || || =
|| | || 检查 || |
|| | || || +
|| | 塞 || 检查 || |
|| | || || +
|| | || 检查 || |
阻 || | 阻 || ||
将数据
|| | || | || |
塞 || |
塞 || ||
从内核
|| | || | || |
|| | || ||
拷贝到
|| | || | || |
|| | || ||
用户空间
|| + || + || +
|| + || || +
|| 完成 || 完成 || 完成 || 完成 || 通知 || +
==========================================================
-------------------------------------------------------------------------------------------- -------------------
第一阶段处理不一样 处理两个阶段
第二阶段处理不一样
(阻塞于recvfrom调用)
同步I/O与异步I/O
POSIX把这两个术语定义以下:
·同步I/O操做(synchronous I/O operation)致使请求进程阻塞,直到I/O操做完成。
·异步I/O(asynchronous I/O operation)不致使请求进程阻塞。
根据上述定义,咱们前4种模型----阻塞I/O模型、非阻塞I/O模型、I/O复用模型和信号去驱动I/O模型都是同步I/O模型,由于其中真正的I/O操做(recvfrom)将阻塞进程。
只有异步I/O模型与POSIX定义的异步I/O相匹配。