5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO
POSIX
同步IO、异步IO、阻塞IO、非阻塞IO,这几个词常见于各种各样的与网络相关的文章之中,往往不同上下文中它们的意思是不一样的,以致于我在很长一段时间对此感到困惑,所以想写一篇文章整理一下。
POSIX(可移植操作系统接口)把同步IO操作定义为导致进程阻塞直到IO完成的操作,反之则是异步IO
按POSIX的描述似乎把同步和阻塞划等号,异步和非阻塞划等号,但是为什么有的人说同步IO不等于阻塞IO呢?先来说说几种常见的IO模型吧。
IO模型
这里统一使用Linux下的系统调用recv作为例子,它用于从套接字上接收一个消息,因为是一个系统调用,所以调用时会从用户进程空间切换到内核空间运行一段时间再切换回来。默认情况下recv会等到网络数据到达并且复制到用户进程空间或者发生错误时返回,而第4个参数flags可以让它马上返回。
- 阻塞IO模型
使用recv的默认参数一直等数据直到拷贝到用户空间,这段时间内进程始终阻塞。A同学用杯子装水,打开水龙头装满水然后离开。这一过程就可以看成是使用了阻塞IO模型,因为如果水龙头没有水,他也要等到有水并装满杯子才能离开去做别的事情。很显然,这种IO模型是同步的。

- 非阻塞IO模型
改变flags,让recv不管有没有获取到数据都返回,如果没有数据那么一段时间后再调用recv看看,如此循环。B同学也用杯子装水,打开水龙头后发现没有水,它离开了,过一会他又拿着杯子来看看……在中间离开的这些时间里,B同学离开了装水现场(回到用户进程空间),可以做他自己的事情。这就是非阻塞IO模型。但是它只有是检查无数据的时候是非阻塞的,在数据到达的时候依然要等待复制数据到用户空间(等着水将水杯装满),因此它还是同步IO。

- IO复用模型
这里在调用recv前先调用select或者poll,这2个系统调用都可以在内核准备好数据(网络数据到达内核)时告知用户进程,这个时候再调用recv一定是有数据的。因此这一过程中它是阻塞于select或poll,而没有阻塞于recv,有人将非阻塞IO定义成在读写操作时没有阻塞于系统调用的IO操作(不包括数据从内核复制到用户空间时的阻塞,因为这相对于网络IO来说确实很短暂),如果按这样理解,这种IO模型也能称之为非阻塞IO模型,但是按POSIX来看,它也是同步IO,那么也和楼上一样称之为同步非阻塞IO吧。
这种IO模型比较特别,分个段。因为它能同时监听多个文件描述符(fd)。这个时候C同学来装水,发现有一排水龙头,舍管阿姨告诉他这些水龙头都还没有水,等有水了告诉他。于是等啊等(select调用中),过了一会阿姨告诉他有水了,但不知道是哪个水龙头有水,自己看吧。于是C同学一个个打开,往杯子里装水(recv)。这里再顺便说说鼎鼎大名的epoll(高性能的代名词啊),epoll也属于IO复用模型,主要区别在于舍管阿姨会告诉C同学哪几个水龙头有水了,不需要一个个打开看(当然还有其它区别)。

- 信号驱动IO模型
通过调用sigaction注册信号函数,等内核数据准备好的时候系统中断当前程序,执行信号函数(在这里面调用recv)。D同学让舍管阿姨等有水的时候通知他(注册信号函数),没多久D同学得知有水了,跑去装水。是不是很像异步IO?很遗憾,它还是同步IO(省不了装水的时间啊)。

- 异步IO模型
调用aio_read,让内核等数据准备好,并且复制到用户进程空间后执行事先指定好的函数。E同学让舍管阿姨将杯子装满水后通知他。整个过程E同学都可以做别的事情(没有recv),这才是真正的异步IO。

总结
IO分两阶段:
1.数据准备阶段
2.内核空间复制回用户进程缓冲区阶段
一般来讲:阻塞IO模型、非阻塞IO模型、IO复用模型(select/poll/epoll)、信号驱动IO模型都属于同步IO,因为阶段2是阻塞的(尽管时间很短)。只有异步IO模型是符合POSIX异步IO操作含义的,不管在阶段1还是阶段2都可以干别的事。
- ps:以上图片均截自UNIX网络编程卷1。
5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO
看了一些文章,发现有很多不同的理解,可能是因为大家入切的角度、环境不一样。所以,我们先说明基本的IO操作及环境。本文是在《UNIX网络编程 卷1:套接字联网API》6.2节"I/O 模型 "的基础上,即UNIX/LINUX环境下的网络 IO环境下的理解,它里面给出的例子是读取(接收)网络UDP数据。下面简单写写自己对这些IO模型的理解。
1、IO
IO (Input/Output,输入/输出)即数据的读取(接收)或写入(发送)操作,通常用户进程中的一个完整IO分为两阶段:用户进程空间<-->内核空间、内核空间<-->设备空间(磁盘、网络等)。IO有内存IO、网络IO和磁盘IO三种,通常我们说的IO指的是后两者。
LINUX中进程无法直接操作I/O设备,其必须通过系统调用请求kernel来协助完成I/O动作;内核会为每个I/O设备维护一个缓冲区。
对于一个输入操作来说,进程IO系统调用后,内核会先看缓冲区中有没有相应的缓存数据,没有的话再到设备中读取,因为设备IO一般速度较慢,需要等待;内核缓冲区有数据则直接复制到进程空间。
所以,对于一个网络输入操作通常包括两个不同阶段:
(1)等待网络数据到达网卡→读取到内核缓冲区,数据准备好;
(2)从内核缓冲区复制数据到进程空间。
2、5种IO模型
《UNIX网络编程》说得很清楚,5种IO模型分别是阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动的IO模型、异步IO模型;前4种为同步IO操作,只有异步IO模型是异步IO操作。下面这样些图,是它里面给出的例子:接收网络UDP数据的流程在IO模型下的分析,在它的基础上再加以简单描述,以区分这些IO模型。
2-1、阻塞IO模型
进程发起IO系统调用后,进程被阻塞,转到内核空间处理,整个IO处理完毕后返回进程。操作成功则进程获取到数据。
1、典型应用:阻塞socket、Java BIO;
2、特点:
进程阻塞挂起不消耗CPU资源,及时响应每个操作;
实现难度低、开发应用较容易;
适用并发量小的网络应用开发;
不适用并发量大的应用:因为一个请求IO会阻塞进程,所以,得为每请求分配一个处理进程(线程)以及时响应,系统开销大。
2-2、非阻塞IO模型
进程发起IO系统调用后,如果内核缓冲区没有数据,需要到IO设备中读取,进程返回一个错误而不会被阻塞;进程发起IO系统调用后,如果内核缓冲区有数据,内核就会把数据返回进程。
对于上面的阻塞IO模型来说,内核数据没准备好需要进程阻塞的时候,就返回一个错误,以使得进程不被阻塞。
1、典型应用:socket是非阻塞的方式(设置为NONBLOCK)
2、特点:
进程轮询(重复)调用,消耗CPU的资源;
实现难度低、开发应用相对阻塞IO模式较难;
适用并发量较小、且不需要及时响应的网络应用开发;
2-3、IO复用模型
多个的进程的IO可以注册到一个复用器(select)上,然后用一个进程调用该select, select会监听所有注册进来的IO;
如果select没有监听的IO在内核缓冲区都没有可读数据,select调用进程会被阻塞;而当任一IO在内核缓冲区中有可数据时,select调用就会返回;
而后select调用进程可以自己或通知另外的进程(注册进程)来再次发起读取IO,读取内核中准备好的数据。
可以看到,多个进程注册IO后,只有另一个select调用进程被阻塞。
1、典型应用:select、poll、epoll三种方案,nginx都可以选择使用这三个方案;Java NIO;
2、特点:
专一进程解决多个进程IO的阻塞问题,性能好;Reactor模式;
实现、开发应用难度较大;
适用高并发服务应用开发:一个进程(线程)响应多个请求;
3、select、poll、epoll
Linux中IO复用的实现方式主要有select、poll和epoll:
Select:注册IO、阻塞扫描,监听的IO最大连接数不能多于FD_SIZE;
Poll:原理和Select相似,没有数量限制,但IO数量大扫描线性性能下降;
Epoll :事件驱动不阻塞,mmap实现内核与用户空间的消息传递,数量很大,Linux2.6后内核支持;
2-4、信号驱动IO模型
当进程发起一个IO操作,会向内核注册一个信号处理函数,然后进程返回不阻塞;当内核数据就绪时会发送一个信号给进程,进程便在信号处理函数中调用IO读取数据。
1、特点:回调机制,实现、开发应用难度大;
2-5、异步IO模型
当进程发起一个IO操作,进程返回(不阻塞),但也不能返回果结;内核把整个IO处理完后,会通知进程结果。如果IO操作成功则进程直接获取到数据。
1、典型应用:JAVA7 AIO、高性能服务器应用
2、特点:
不阻塞,数据一步到位;Proactor模式;
需要操作系统的底层支持,LINUX 2.5 版本内核首现,2.6 版本产品的内核标准特性;
实现、开发应用难度大;
非常适合高性能高并发应用;
3、IO模型比较
3-1、阻塞IO调用和非阻塞IO调用、阻塞IO模型和非阻塞IO模型
注意这里的阻塞IO调用和非阻塞IO调用不是指阻塞IO模型和非阻塞IO模型:
阻塞IO调用:在用户进程(线程)中调用执行的时候,进程会等待该IO操作,而使得其他操作无法执行。
非阻塞IO调用:在用户进程中调用执行的时候,无论成功与否,该IO操作会立即返回,之后进程可以进行其他操作(当然如果是读取到数据,一般就接着进行数据处理)。
这个直接理解就好,进程(线程)IO调用会不会阻塞进程自己。所以这里两个概念是相对调用进程本身状态来讲的。
从上面对比图片来说,阻塞IO模型是一个阻塞IO调用,而非阻塞IO模型是多个非阻塞IO调用+一个阻塞IO调用,因为多个IO检查会立即返回错误,不会阻塞进程。
而上面也说过了,非阻塞IO模型对于阻塞IO模型来说区别就是,内核数据没准备好需要进程阻塞的时候,就返回一个错误,以使得进程不被阻塞。
3-2、同步IO和异步IO
同步IO:导致请求进程阻塞,直到I/O操作完成。
异步IO:不导致请求进程阻塞。
上面两个定义是《UNIX网络编程 卷1:套接字联网API》给出的。这不是很好理解,我们来扩展一下,先说说同步和异步,同步和异步关注的是双方的消息通信机制:
同步:双方的动作是经过双方协调的,步调一致的。
异步:双方并不需要协调,都可以随意进行各自的操作。
这里我们的双方是指,用户进程和IO设备;明确同步和异步之后,我们在上面网络输入操作例子的基础上,进行扩展定义:
同步IO:用户进程发出IO调用,去获取IO设备数据,双方的数据要经过内核缓冲区同步,完全准备好后,再复制返回到用户进程。而复制返回到用户进程会导致请求进程阻塞,直到I/O操作完成。
异步IO:用户进程发出IO调用,去获取IO设备数据,并不需要同步,内核直接复制到进程,整个过程不导致请求进程阻塞。
所以, 阻塞IO模型、非阻塞IO模型、IO复用模型、信号驱动的IO模型者为同步IO模型,只有异步IO模型是异步IO。
5种IO模型、阻塞IO和非阻塞IO、同步IO和异步IO的更多相关文章
- IO模型《三》非阻塞IO
非阻塞IO(non-blocking IO) Linux下,可以通过设置socket使其变为non-blocking.当对一个non-blocking socket执行读操作时,流程是这个样子: 从图 ...
- Socket 阻塞模式和非阻塞模式
阻塞I/O模型: 简介:进程会一直阻塞,直到数据拷贝 完成 应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好. 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返 ...
- 并发式IO的解决方案:多路非阻塞式IO、多路复用、异步IO
在Linux应用编程中的并发式IO的三种解决方案是: (1) 多路非阻塞式IO (2) 多路复用 (3) 异步IO 以下代码将以操作鼠标和键盘为实例来演示. 1. 多路非阻塞式IO 多路非阻塞式IO访 ...
- 阻塞式和非阻塞式IO
有很多人把阻塞认为是同步,把非阻塞认为是异步:个人认为这样是不准确的,当然从思想上可以这样类比,但方式是完全不同的,下面说说在JAVA里面阻塞IO和非阻塞IO的区别 在JDK1.4中引入了一个NIO的 ...
- Linux下同步模式、异步模式、阻塞调用、非阻塞调用总结
转自:http://www.360doc.com/content/13/0117/12/5073814_260691714.shtml 同步和异步:与消息的通知机制有关. 本质区别 现实例子 同步模式 ...
- 斐讯面试记录—阻塞Socket和非阻塞Socket
文章出自:http://blog.csdn.net/VCSockets/ 1.TCP中的阻塞Socket和非阻塞Socket 阻塞与非阻塞是对一个文件描述符指定的文件或设备的两种工作方式. 阻塞的意思 ...
- Verilog HDL中阻塞语句和非阻塞语句的区别
在Verilog中有两种类型的赋值语句:阻塞赋值语句(“=”)和非阻塞赋值语句(“<=”).正确地使用这两种赋值语句对于Verilog的设计和仿真非常重要. Verilog语言中讲的阻塞赋值与非 ...
- [Python 多线程] Lock、阻塞锁、非阻塞锁 (八)
线程同步技术: 解决多个线程争抢同一个资源的情况,线程协作工作.一份数据同一时刻只能有一个线程处理. 解决线程同步的几种方法: Lock.RLock.Condition.Barrier.semapho ...
- socket异步通信-如何设置成非阻塞模式、非阻塞模式下判断connect成功(失败)、判断recv/recvfrom成功(失败)、判断send/sendto
socket异步通信-如何设置成非阻塞模式.非阻塞模式下判断connect成功(失败).判断recv/recvfrom成功(失败).判断send/sendto 博客分类: Linux Socket s ...
- Java基础知识强化之多线程笔记07:同步、异步、阻塞式、非阻塞式 的联系与区别
1. 同步: 所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调用就不返回.但是一旦调用返回,就必须先得到返回值了. 换句话话说,调用者主动等待这个"调用"的结果. 对于 ...
随机推荐
- c# winfrom 更新控件时停止刷新,解决闪烁问题
static Dictionary<Control, bool> m_lstFreezeControl = new Dictionary<Control, bool>(); / ...
- ABAP 7.4 新语法-内嵌生命和内表操作(转)
转自:https://www.cnblogs.com/mingdashu/p/6744637.html ABAP 7.4 新语法-内嵌生命和内表操作 1.内嵌声明 2.内表操作 3.opensql ...
- UserControl关闭
直接 Application.Current.Shutdown();关闭程序.
- JMeter处理form-data类型的接口
最近的需求中,有的接口入参是form-data类型的,除了用python多进程代码进行压测,考虑用Jmeter试试看,比对一下结果. 线程数设置的是50,循环次数为100,一共发送5000次请求. H ...
- export default和export的使用
export default和export都是用来向外暴露成员 export default 向外暴露的成员可以使用任意的变量来接收,在一个模块中,export default只允许向外暴露一次,可以 ...
- JS中的NaN和isNaN,简直是双重人格?
number数字类型 包括数字和NaN,NaN:not a number 但是它是数字类型的 isNaN的用法:检测当前值是否不是有效数字,返回true代表不是有效数字,返回false是有效数字 ...
- 双十一DIY装机记
一.装机背景 最近发现古董笔记本太卡了,用了近6年,尽管自己不打游戏,但是业余时间写写代码,同时开两个编辑器,打开个大一点的软件都卡的不行,据说更换固态硬盘可以提高速度,于是乎,京东买了一个500 ...
- 安全意识第三期丨关于高速ETC办理的这些新骗局,速看!
近期,最火爆的莫过于ETC了. 不仅各大银行,甚至微信和支付宝都推出了办理服务. 虽说更加便捷了,却也带来了安全隐患. 下面这个案例,大家一定要注意,已经有很多车主“中招”,落入了骗子的圈套. 注意: ...
- 查找发布地图的 REST URL并查询相关信息
1.登录ArcGIS Server Manager 2.登录后,里面是以前自己发布的地图服务 3.点击自己发布的地图,然后按下功能选项,再点击箭头来找到URL 4.点击进去,分别能从红圈中找到相关的信 ...
- Spinner在Dialog中的使用效果
版权声明:本文为xing_star原创文章,转载请注明出处! 本文同步自http://javaexception.com/archives/91 背景: 记得很久以前,碰到一个需求场景,需要在Andr ...