您的位置:首页 > 其它

IO操作:同步,异步,阻塞,非阻塞

2017-08-29 13:02 232 查看
思考:

编程讨论的I/O模型,一般是在网络通信方面

对I/O模型的理解抓住一个关键点,I/O操作分两步:

1、等待数据准备好

2、将数据从内核空间拷贝到用户空间

a) 阻塞和非阻塞I/O,一般讨论的是第一阶段,等待数据准备好的策略。阻塞I/O的意思是,一直等待数据准备好,才进行到下一步;非阻塞I/O,查看数据是否准备好,如果在规定时间内还没准备好,那么就返回错误,如果数据准备好了,就进行下一步。

b) 此外还有信号驱动式I/O,此方式一旦调用就立刻返回,如果数据就绪,内核会发送一个信号给调用进程,然后进程执行到下一步。信号驱动式I/O和后面将要提到的异步I/O有相似的地方,区别是:信号驱动式I/O,内核发送信号告诉进程,什么时候启动IO操作,I/O操作本身还是阻塞的;异步I/O是,内核发送信号告诉进程数据读取什么时候完成,不会出现I/O阻塞的情况。

c) I/O多路复用:虽然I/O多路复用的函数也是阻塞的,但是其与以上两种还是有不同的,I/O多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用如recvfrom之上。也就是,I/O多路复用在等待数据准备好这一阶段,针对单个I/O口并不会阻塞,但是将数据从内核空间拷贝到用户空间依旧会阻塞进程。

d) 异步I/O:调用异步I/O系统立刻返回,等到 等待数据准备好,将数据从内核空间拷贝到用户空间这两步操作都完成,内核才向进程发送信号。也就是整个操作(包括等待数据准备好,从内核拷贝数据均不在调用进程里面执行)

总结:

同步I/O和异步I/O区别在第二步:同步I/O在将数据从内核拷贝到用户空间时,会出现阻塞;而异步I/O,在将数据从内核拷贝到用户空间时不是调用进程完成,因此不会阻塞进程。


阻塞I/O,非阻塞I/O、信号驱动I/O、I/O多路复用,都属于同步I/O。

同步 :调用进程自己处理I/O读写
异步 :使用异步I/O时,将I/O读写委托给内核处理

Java对BIO、NIO、AIO的支持:

BIO :同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

NIO :同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

AIO :异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,

BIO、NIO、AIO适用场景分析:

BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

既然网络上众说纷纭,不如找个权威参考一下,这个权威就是《UNIX网络编程:卷一》第六章——I/O复用。书中向我们提及了5种类UNIX下可用的I/O模型:

阻塞式I/O;

非阻塞式I/O;

I/O复用(select,poll,epoll...);

信号驱动式I/O(SIGIO);

异步I/O(POSIX的aio_系列函数);

阻塞式I/O模型:默认情况下,所有套接字都是阻塞的。怎么理解?先理解这么个流程,一个输入操作通常包括两个不同阶段:

(1)等待数据准备好;

(2)从内核向进程复制数据。

对于一个套接字上的输入操作,第一步通常涉及等待数据从网络中到达。当所有等待分组到达时,它被复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用程序缓冲区。 好,下面我们以阻塞套接字的recvfrom的的调用图来说明阻塞





标红的这部分过程就是阻塞,直到阻塞结束recvfrom才能返回。

非阻塞式I/O: 以下这句话很重要:进程把一个套接字设置成非阻塞是在通知内核,当所请求的I/O操作非得把本进程投入睡眠才能完成时,不要把进程投入睡眠,而是返回一个错误。看看非阻塞的套接字的recvfrom操作如何进行





可以看出recvfrom总是立即返回。

I/O多路复用:虽然I/O多路复用的函数也是阻塞的,但是其与以上两种还是有不同的,I/O多路复用是阻塞在select,epoll这样的系统调用之上,而没有阻塞在真正的I/O系统调用如recvfrom之上。如图





信号驱动式I/O:用的很少,就不做讲解了。直接上图





异步I/O:这类函数的工作机制是告知内核启动某个操作,并让内核在整个操作(包括将数据从内核拷贝到用户空间)完成后通知我们。如图:





注意红线标记处说明在调用时就可以立马返回,等函数操作完成会通知我们。

等等,大家一定要问了,同步这个概念你怎么没涉及啊?别急,您先看总结。 其实前四种I/O模型都是同步I/O操作,他们的区别在于第一阶段,而他们的第二阶段是一样的:在数据从内核复制到应用缓冲区期间(用户空间),进程阻塞于recvfrom调用。相反,异步I/O模型在这两个阶段都要处理。





再看POSIX对这两个术语的定义:

同步I/O操作:导致请求进程阻塞,直到I/O操作完成;

异步I/O操作:不导致请求进程阻塞。

好,下面我用我的语言来总结一下阻塞,非阻塞,同步,异步

阻塞,非阻塞:进程/线程要访问的数据是否就绪,进程/线程是否需要等待;

同步,异步:访问数据的方式,同步需要主动读写数据,在读写数据的过程中还是会阻塞;异步只需要I/O操作完成的通知,并不主动读写数据,由操作系统内核完成数据的读写。

转载自:http://yaocoder.blog.51cto.com/2668309/1308899
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: