第一章 Java I/O
1.1 I/O基础入门
Java1.4之前的版本,开发高性能I/O程序的时候,有问题:
- 没有数据缓冲区,I/O性能有问题
- 没有Channel概念,只有输入输出流
- 只有BIO,通常会导致通信线程被长时间阻塞
- 支持字符集有限,硬件移植性不好
1.1.1 Linux网络I/O模型
Linux的内核将所有的外部设备都看做一个文件来操作,对一个文件的读写操作会调用内核提供的系统提供的系统命令,返回一个file descriptor(fd,文件描述符)。而对一个socket的读写也有相应的描述符,称为socketfd(socket描述符),描述符就是一个数字,它指向内核中的一个结构体(文件路径,数据区等一些属性)。
Unix提供了五种I/O模型:
- 阻塞I/O模型:所有文件操作都是阻塞的,
- 非阻塞I/O模型:recvfrom时,如果缓冲区没有数据就返回一个EWOULDBLOCK错误,然后一直轮询检查
- I/O复用模型:Linux提供select/poll,进程通过将一个或多个fd传递给Select或poll,阻塞到Select上,这样就可以检测多个fd是否处于就绪状态。还提供了epoll,基于事件驱动代替select和poll的顺序扫描
- 信号驱动I/O模型:需要开启套接口信号驱动I/O功能,通过系统调用sigaction执行信号处理函数,接收到信号就回调然后recvfrom执行。
- 异步I/O:告知内核启动某个操作,内核完成操作后再通知。
1.1.2 I/O多路复用
可以同时处理多个客户端请求,把多个I/O阻塞复用到同一个Select的阻塞上,不需要新的进程或线程,系统开销小
select有缺陷,epoll改进:
-
一个进程打开的socketfd不受限制,仅受限于操作系统的最大文件句柄数
-
I/O效率不会随着FD的数目增加而线性下降
epoll只会对活跃的socket进行操作,因为epoll是根据每个fd的callback函数实现的,只有活跃的socket才会去主动调用这个函数。
-
使用mmap加速内核与用户空间的消息传递
-
epoll的api更加简单
1.2 Java的I/O演进
JDK1.4推出NIO,主要的类和接口:
- 缓冲区ByteBuffer
- 管道Pipe
- 进行I/O操作的Channel,包括ServerSocketChannel和SocketChannel
- 多种字符集的编码和解码能力
- 实现非阻塞I/O操作的多路复用器selector
- 基于流行的Perl实