IO
IO一般说的就是IO流了,IO流一般是从磁盘或者其他主机上去读取或者写入数据,当然,除了磁盘,还有网络,内存都是可以作为IO流的数据的来源或者目的地。再Java中也提供了字节流或者字符流去实现这种对数据流的操作。那么如果是面向网络的话,Java中也提供了这种对TCP/IP协议的封装的这种接口,叫Socket,通过Socket我们就可以实现数据再网络上的传递。基于Socket的IO通讯,其实它是一种阻塞IO,阻塞IO也就是我们所说的BIO,也就是我们说的IO的概念。而阻塞IO的概念的意思就是,一旦某个连接处于阻塞状态,那么后续的连接都需要等待,直到这个连接完成了对应的工作任务之后,服务器才会去响应处理其他的IO请求。
NIO
再JDK1.4之前,只有IO这一种选择,而为了解决越来越大的并发的问题,再JDK1.4之后推出了NIO(New IO/Non Blocking IO),它是一种新的IO机制,它是非阻塞的,相比于传统IO,非阻塞NIO再效率上做了很大的优化,比如加了一些核心的组件,然后又提供了非阻塞这种特性,所以使用NIO进行网络数据传输的时候,即便当前的这个IO事件并没有完成,服务器端也并不会阻塞当前的连接,后续的连接也可以继续跟进,因此并行处理的连接数量会比BIO多得多。
总而言之,IO和NIO的区别,如果站在网络IO的角度上来看,其实是很形象的,前者是阻塞IO,后者就是非阻塞IO。
Linux上的IO模型
Linux支持的IO有BIO,NIO,AIO,其中AIO是一种异步IO。
按照Unix的模型划分,I/O模型可以分为:阻塞I/O模型、非阻塞I/O模型、I/O复用模型、信号
驱动式I/O模型和异步I/O模型,按照POSIX标准来划分只分为两类:同步I/O和异步I/O。
如何区分呢?首先一个I/O操作其实分成了两个步骤:发起IO请求和实际的IO操作。同步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请求是否会被阻塞,如果阻塞直到完成那么就是传统的
阻塞I/O,如果不阻塞,那么就是非阻塞I/O。
对于阻塞I/O模型 ,在linux中,默认情况下所有的socket都是blocking阻塞的。
对于非阻塞I/O模型,在linux下,可以通过设置socket使其变为non-blocking。当对一个non-blocking
socket执行读操作时,如果没有数据准备好,那么就立刻返回,如果数据准备好了,那么就立刻复制数据报。
对于IO复用模型,可以调用select或者poll,那么系统会阻塞再这两个系统调用的某一个之上,而不是直接阻塞在真正的IO系统调用上。
对于信号驱动式I/O模型,我们可以用信号,让内核在描述符就绪时发送SIGIO信号通知我们,如果数据准备好了,那么内核发送一个SIGIO信号给我们的信号处理程序,那么此时我们的应用进程就知道可以去读取复制数据了。
对于异步I/O模型,用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从内核
的角度,当它受到一个asynchronousread之后,首先它会立刻返回,所以不会对用户进程产生任
何block。然后,内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,
内核会给用户进程发送一个signal,告诉它read操作完成了,此时数据已经在内存中了,等待应用进程去读取。
从前面 I/O 模型的分类中,我们可以看出 AIO 的动机。阻塞模型需要在 I/O 操作开始时阻塞应用程序。这意味着不可能同时重叠进行处理和 I/O 操作。
非阻塞模型允许处理和 I/O 操作重叠进行,但是这需要应用程序来检查 I/O 操作的状态。
对于异步I/O ,它允许处理和 I/O 操作重叠进行,包括 I/O 操作完成的通知。除了需要阻塞之外,select 函数所提供的功能(异步阻塞 I/O)与 AIO 类似。不过,它是对通知
事件进行阻塞,而不是对 I/O 调用进行阻塞。
同时Linux还支持多路复用,其中比较经典的就是epoll模型了。