I/O 就是输入和输出,为了保证操作系统的稳定性和安全性,一个进程的地址空间分为用户空间和内核空间。平时运行的应用程序都是在用户空间内,内核空间进行系统级别的资源有关的操作。比如果文件管理,进行通信等。用户空间的程序不饿能直接访问内核空间。

当程序执行 IO 操作时,应用程序发起调用请求让操作系统帮助。

开发中接触的比较多的就是磁盘 IO 、文件读写和 网络 IO、网络请求和响应。

从应用程序来看,应用程序就是向操作系统的内核空间发起调用请求,操作系统的内核执行实际的 IO 操作。相当于应用程序发起 IO 操作的调用,实际 IO 执行是在内核完成的。

应用程序调用之后会有两个步骤:

1. 内核等待 IO 设备准备好资源数据。

2. 内核将准备好的数据从内核空间拷贝到用户空间。

常见的 IO 模型,同步阻塞 IO、同步非阻塞 IO、IO 多路复用、信号驱动 IO、异步 IO 。

这里只看 Java 中常见的模型。

同步阻塞 IO(BIO)

应用程序调用 IO 请求之后,操作系统内核会有从准备数据到数据拷贝的过程,这个过程进程是被阻塞的,当内核将请求返回进程才能继续执行之后的工作。

同步阻塞IO(BIO)就是在应用程序发起 read 调用之后,直到内核将数据拷贝到用户空间时 返回read 请求。整个调用过程是阻塞的。

java 大模型 api 支持流式输出吗 java io模型详解_多路复用

这种模型在高并发环境下就会性能低下。

同步非阻塞IO (NIO)

简单来说,同步非阻塞就是当应用程序发起 read 调用后,不用等待 IO 设备准备资源数据,而是直接接收内核传来的返回。期间多次访问内核有没有将数据准备好,直到 IO 设备准备好了资源数据,内核就将数据拷贝给用户空间。

这个模型的问题就是,期间多次调用 read,这是会消耗资源的。

java 大模型 api 支持流式输出吗 java io模型详解_多路复用_02

考虑到多次调用过多消耗资源,就可以使用 IO 多路复用模型。

IO 多路复用

所谓多路复用,就是将线程多次复用,由一个或者多个线程监控 IO 请求,一旦某个请求就绪了,就通知程序可以进行 IO 操作了,此时就是将内核中的数据拷贝到用户空间了。

目前支持 IO 多路复用的系统调用,由 select、epoll等。首先单个线程不断通过 select / epoll 系统调用轮询连接,当有某个连接地数据准备好了之后,就通知应用程序可以调用了,然后发起 read 调用,将内核中的数据拷贝到用户空间。

java 大模型 api 支持流式输出吗 java io模型详解_应用程序_03

IO 多路复用和 NIO 的区别也就是优势,多路复用通过 select / epoll 来监听多个连接,一个线程对应多个任务,而 NIO 是一条线程轮询一个任务。开销明显的减少了。

但是 IO 多路复用在数据拷贝期间仍然是线程阻塞的,这里就可以用到异步 IO (AIO)

异步 IO(AIO)

简单来说,就是用户调用 read 之后直接返回,然后去做其他的事情,数据准备和拷贝都交由内核空间来完成,流程完毕之后,通知用户程序,可以执行之后的操作了。

就好比你加班回家非常饿,家人给你留的饭菜已经冷了,这个时候你又想洗澡放松一下,你就可以调用方法请求到微波炉,微波炉给你返回一个信息告诉你我在给你准备晚饭了,此时你可以先去洗个澡,直到听到微波炉 “ 叮 ” 的响应,然后你就可以开始吃饭了。 

java 大模型 api 支持流式输出吗 java io模型详解_java_04

异步 IO 需要底层操作系统的支持,因为大量的工作都是在内核空间完成的。

目前应用不是很广泛,大多数还是使用的多路复用 IO,比如 Redis。