同步IO、异步IO、阻塞IO、非阻塞IO,这几个词常见于各类各样的与网络相关的文章之中,每每不一样上下文中它们的意思是不同的,以至于我在很长一段时间对此感到困惑,因此想写一篇文章整理一下。编程
POSIX(可移植操做系统接口)把同步IO操做定义为致使进程阻塞直到IO完成的操做,反之则是异步IO
按POSIX的描述彷佛把同步和阻塞划等号,异步和非阻塞划等号,可是为何有的人说同步IO不等于阻塞IO呢?先来讲说几种常见的IO模型吧。网络
这里统一使用Linux下的系统调用recv做为例子,它用于从套接字上接收一个消息,由于是一个系统调用,因此调用时会从用户进程空间切换到内核空间运行一段时间再切换回来。默认状况下recv会等到网络数据到达而且复制到用户进程空间或者发生错误时返回,而第4个参数flags可让它立刻返回。异步
使用recv的默认参数一直等数据直到拷贝到用户空间,这段时间内进程始终阻塞。A同窗用杯子装水,打开水龙头装满水而后离开。这一过程就能够当作是使用了阻塞IO模型,由于若是水龙头没有水,他也要等到有水并装满杯子才能离开去作别的事情。很显然,这种IO模型是同步的。函数
改变flags,让recv无论有没有获取到数据都返回,若是没有数据那么一段时间后再调用recv看看,如此循环。B同窗也用杯子装水,打开水龙头后发现没有水,它离开了,过一会他又拿着杯子来看看……在中间离开的这些时间里,B同窗离开了装水现场(回到用户进程空间),能够作他本身的事情。这就是非阻塞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同窗哪几个水龙头有水了,不须要一个个打开看(固然还有其它区别)。code
经过调用sigaction注册信号函数,等内核数据准备好的时候系统中断当前程序,执行信号函数(在这里面调用recv)。D同窗让舍管阿姨等有水的时候通知他(注册信号函数),没多久D同窗得知有水了,跑去装水。是否是很像异步IO?很遗憾,它仍是同步IO(省不了装水的时间啊)。blog
调用aio_read,让内核等数据准备好,而且复制到用户进程空间后执行事先指定好的函数。E同窗让舍管阿姨将杯子装满水后通知他。整个过程E同窗均可以作别的事情(没有recv),这才是真正的异步IO。接口
IO分两阶段:进程
1.数据准备阶段 2.内核空间复制回用户进程缓冲区阶段
通常来说:阻塞IO模型、非阻塞IO模型、IO复用模型(select/poll/epoll)、信号驱动IO模型都属于同步IO,由于阶段2是阻塞的(尽管时间很短)。只有异步IO模型是符合POSIX异步IO操做含义的,无论在阶段1仍是阶段2均可以干别的事。