Java NIO编程理论基础篇——Java I/O的发展以及linux网络I/O模型

该篇文章的内容和图片参考李林锋先生的《Netty权威指南》

Java I/O发展概述:

Java在jdk1.4之前,Java对I/O的支持并不完善,以致在进行高性能服务端开发时难度较大,导致高性能服务端领域长时间被C++和C占据。这些问题主要是:
没有数据缓冲区,性能较低;
没有C或者C++中的Channel概念,只有输入输出流;
同步阻塞式I/O通信,可能导致线程长时间阻塞;
支持的字符集有限,硬件可移植性不好。

在jdk1.4中新增了java.nio包,提供了进行异步I/O开发的API和类库,主要的类和接口如下:

进行异步I/O操作的缓冲区XXXBuffer;
进行异步I/O操作的管道Pipe;
进行各种I/O操作的Channel;
多种字符集的编解码能力;
实现非阻塞I/O操作的多路复用器selector;
基于流行的Perl实现的正则表达式类库;
文件通道FileChannel。

极大的促进了异步非阻塞编程的发展和应用,但是在文件系统的处理能力仍显不足,在jdk1.7发布后,nio升级为nio2.0,进行了如下改进:

提供能够批量获取文件属性的API,这些API具有平台无关性,不与特性的文件系统相耦合,并且提供了标准文件系统SPI,供各个服务提供商扩展实现;
提供AIO功能,支持基于异步I/O操作和针对网络套接字的异步操作;
完成通道功能,包括对配置和多薄数据报的支持等。

Linux网络I/O模型

Linux内核将所有外部设备作为一个文件进行处理,对每个文件的读写操作都会调用内核提供的系统命令,返回一个文件描述符fd(file descripter),而对于一个socket的读写也有相应的描述符,称为socket描述符socketfd,描述符就是一个数字,它指向内核中的一个结构体(文件路径,数据区等一些属性)。 根据UNIX网络编程对I/O模型的分类,共提供了5中I/O模型,如下: 【我们使用系统调用recvfrom函数进行举例,recvfrom的功能是从(已连接)套接口上接收数据,并捕获数据发送源的地址】
1.阻塞I/O模型: 一开始学Java基础时就是使用的阻塞I/O模型,在缺省情况下(系统命令有相应的参数可以设置,这里不多赘述),所有文件操作都是阻塞的。在调用系统命令后,需要准备的数据到达之后再去执行操作,在等待数据的期间需要一直等待。如图:

Java NIO编程理论基础篇——Java I/O的发展以及linux网络I/O模型_第1张图片

2.非阻塞I/O模型:
recvfrom从应用层到内核的时候,如果该缓冲区没有数据,直接返回一个EWOULDBLOCK错误,并以轮询的方式查看缓冲区中是否有准备好的数据。如图:
Java NIO编程理论基础篇——Java I/O的发展以及linux网络I/O模型_第2张图片

3.I/O复用模型:
linux提供了三组I/O复用系统:select/poll/epoll。
select/poll方式:进程通过fd传递给select/poll系统调用,阻塞在select上,这样select/poll就可以帮我们监听多个fd是否处于就绪状态,select/poll是顺序扫描fd是否就绪,且支持的fd有限,因此它的使用受到了一定的制约;
epoll方式:使用基于事件驱动的方式进行扫描,当有fd就绪时,立即回调函数rollback,因此性能更高。epoll相对于select/poll还有以下改进:
1)支持一个进程打开的socket描述符不受限制,上限是操作系统的最大文件句柄数,比如一个1GB的机器上大约是10万个左右句柄,具体的值可以通过cat/proc/sys/fs/file-max查看,通常与内存大小相关(我的个人pc结果是1099093);
2)I/O效率不会随着FD数目的增加而线性下降 ,epoll是根据每个fd上面的callback函数实现的,而非先行扫描,对于“活跃”的socket才会主动调用callback函数,从而在大量空闲socket接入后不会导致明显性能下降。
3)使用mmap加速内核与用户空间的消息传递 ,无论是select/poll/epoll都需要内核把fd消息通知给用户空间,epoll通过内核和用户空间mmap使用同一块内存实现避免不必要的内存复制。
4)epoll的API更加简单
如图:
Java NIO编程理论基础篇——Java I/O的发展以及linux网络I/O模型_第3张图片

NIO的核心类库中的选择器Selector就是基于epoll实现的,I/O多路复用技术通过把多个I/O的阻塞复用到一个select的阻塞上,从而使得系统在单线程的情况下可以同时处理多个客户端请求。I/O多路复用最大的优点就是系统开销小,也不需要维护这些进程和线程的运行,降低了系统的维护工作量,节省系统资源。I/O多路复用主要使用场所:

服务器需要同时处理多个处于监听状态或者多个连接状态的套接字;
服务器需要同时处理多种网络协议的套接字。

4.信号驱动I/O模型:
首先开启套接口信号驱动I/O功能,并通过系统调用sigaction至i先嗯一个信号处理函数。当准备就绪时,就为该进程生成一个SIGIO信号,通过信号回调通知应用程序调用recvfrom来读取数据并通知主函数处理数据。如图:
Java NIO编程理论基础篇——Java I/O的发展以及linux网络I/O模型_第4张图片

5.异步I/O模型:
告知内核启动某个操作,并让内核在整个操作完成后通知我们(包括将数据从内核复制到用户自己的缓冲区),信号驱动和异步的区别在于:信号驱动由内核同志我们何时可以开始一个I/O操作,而异步模型通知我们何时操作已经完成。如图:
Java NIO编程理论基础篇——Java I/O的发展以及linux网络I/O模型_第5张图片

在对各种I/O模型有了一定了解的知识基础之后,就可以开始进一步的学习啦!
如果对Linux网络编程或者I/O多路复用想有更多的了解可以参考小伙伴的博客:
I/O多路复用之select
I/O多路复用之poll
I/O多路复用之epoll
I/O多路复用之比较select&poll&epoll

你可能感兴趣的:(#,Java网络编程)