五种 IO 模型
在 操作系统中,IO 模型有如下五种:
在上文已经说到,其实 IO 操作就是将数据在用户空间与内核空间进行相互转换,这个过程是通过系统调用
来完成的。 IO 技术的发展目标就是如何使用尽可能少的资源来完成数据的传输,这里资源主要就是指 CPU 资源。
无论是文件 IO,还是网络 IO,最后都可以统一为用户空间和内核空间数据的交换。
BIO
BIO 是最经典的一种 IO 方式,也是最简单粗暴的方式,在发起 IO 操作之后,当前调用线程就会处在阻塞状态,直到数据传输完成。
NIO
NIO 是在 BIO 基础之上的一个改进,NIO 在数据还未准备好的情况下,不会阻塞进程,而是通过轮询的方式,不断的去查询数据时候准备好,当数据可以被读取时,当前线程就会处在阻塞状态,直到数据读取完成。
所以 NIO 中的非阻塞指的是在等待数据的阶段,实际进行数据传输时,还是阻塞的,这点需要注意。
IO 多路复用
IO 多路复用是对 NIO 的一个改进,在 NIO 中,需要不断轮询查看数据是否准备好,IO 多路复用的改进是不再主动去查询数据状态是否准备完成,而是等数据准备好的通知,当数据准备完成之后,才会开始传输数据。
与 NIO 一样,在数据的传输阶段,当前线程依然是阻塞的。
在 Linux 系统中,IO 多路复用的方式有多种:
select
poll
epoll
信号驱动 IO
信号驱动 IO 通过 sigaction 系统调用,向内核发送一个信号,当内核中数据准备好之后,当前线程也会接收到一个信号,在这个过程中,当前线程也是非阻塞的。在接收到信号之后,就可以开始传输数据。
AIO
上面的这些 IO 模型虽然有些号称是不阻塞的,那是指在等待数据就绪的过程中是不阻塞的,但是在接收数据的时候,依然还是阻塞的。
AIO 是这些 IO 模型中真正实现完全不阻塞,AIO 在被调用之后直接返回,连接收数据的阶段也是非阻塞的,等到数据接收完成之后,内核才会返回一个通知,也就是说当用户进程接收到通知时,数据已经接收完成。
在 Linux 中提供了 AIO 的实现,但是实际上使用的并不多,更多还是使用独立的异步 IO 库,比如libevent、libev、libuv。
五种 IO 模式的总结如下:
Java 中 IO 的实现
Java 中的 IO 也不例外,实际的 IO 是调用了系统的能力来完成,在用户态通过系统调转到内核态,最终实现文件的读写或者通信。
Java 中 IO 就是典型的 BIO,而且 NIO 则不是对应五种 IO 模型中的 NIO,Java 中的 NIO 实际上是使用 IO 多路复用来实现的。
Java 中的 NIO2 也称之为 AIO,正是对应操作系统中的 AIO,当然具体的实现可能是其他的库。