详解五种IO模型

详解五种IO模型

IO 模型是网络编程的基础,几乎所有的高性能的中间件都会提到使用了高效的 IO 模型(Redis、Kafka、Tomcat、Nginx 等)。

前言

Unix 系统下的五种基本 I/O 模型大家应该都有所耳闻,分别是:

  • blocking I/O(同步阻塞IO,BIO)
  • nonblocking I/O(同步非阻塞IO,NIO)
  • I/O multiplexing (I/O多路复用)
  • signal driven I/O(信号驱动I/O)
  • asynchronous I/O(异步I/O,AIO)

一次网络 IO 的流程如下:

真正的I/O过程,主要分为下面两个阶段,所有 I/O 模型的区别就在这两个阶段上。

  • 用户线程等待内核将数据从网卡拷贝到内核空间。
  • 内核将数据从内核空间拷贝到用户空间。

同步阻塞 IO

用户线程发起 read 调用后就阻塞了,让出 CPU。内核等待网卡数据到来,把数据从网卡拷贝到内核空间,接着把数据拷贝到用户空间,再把用户线程叫醒。

多线程可以提高 BIO 的吞吐量,因为在阻塞等待数据返回用户态的过程中 CPU 可以执行其它线程的任务。

一般为了避免过多的创建线程占据系统资源,系统中会使用"线程池"或者"连接池"减少创建和销毁线程的频率,让空闲的线程重新承担新的执行任务,维持一个合理的线程数量,降低系统开销。

但由于 BIO 每个线程负责一份 IO 请求的特性,当 IO 请求数量过大时就无能为力了~

BIO 在 IO 的两阶段都是阻塞的。

同步非阻塞 IO

用户线程不断的发起 read 调用,数据没到内核空间时,每次都返回失败,直到数据到了内核空间,这一次 read 调用后,在等待数据从内核空间拷贝到用户空间这段时间里,线程还是阻塞的,等数据到了用户空间再把线程叫醒。

所以在非阻塞式 IO 中,用户进程其实是需要不断地主动询问 kernel 数据准备好了没有,但是这样采用轮询方式,会导致系统上下文切换开销很大,大幅度提高 CPU 占用率。

因此,单独使用非阻塞 I/O 模型的效率并不高。而且随着并发量的提升,非阻塞 I/O 会存在严重的性能浪费。

该模型轮询的目的只是检测"单个 IO 数据是否已经就绪",操作系统提供了更为高效的检测接口,一次检测多个 IO 数据是否已经就绪,这就是接下来的 IO 多路复用。

同步非阻塞 IO 是一阶段非阻塞,二阶段阻塞。

多路复用 IO

多路复用实现了一个线程处理多个 I/O 句柄的操作,这种 IO 方式也被称为事件驱动 IO(event driven IO)。

  • 多路 指的是多个数据通道
  • 复用 指的是使用一个或多个固定线程来处理每一个 Socket。

大名鼎鼎的 select、poll、epoll 就是该模式的不同实现。

多个的进程的 IO 可以注册到一个复用器(selector)上,然后用一个进程调用 select,select 会监听所有注册进来的 IO。

如果 selector 所有监听的 IO 在内核缓冲区都没有可读数据,select 调用进程会被阻塞(也可以设置超时时间,超过后就返回);同时,kernel 会"监视"本次 select 负责的 socket,如果任何一个 socket 中的数据准备好了,select 就会返回;之后就由业务进程对有 IO 事件发生的 socket 进行处理了。

多路复用 IO 模型只有一个 select 调用进程被阻塞,它也是非阻塞 IO,因为用户线程阻塞在 select 方法上,不像其他 IO 阻塞在 read write 方法调用。

多路复用解决了同步阻塞 I/O 和同步非阻塞 I/O 最大的问题(即并发量上来后也无需创建多个进程),单个 process 就可以同时处理多个网络连接的 IO。不过如果处理的 IO 数不多的情况下,使用多路复用 IO 的 web server 不一定比使用 池化+BIO 的 web server 性能更好,可能延迟还更大。考虑极端情况下,只有一个IO,多路复用需要 2 次系统调用(select + recvfrom),而BIO只需要 1 次系统调用(recvfrom)。

所以,多路复用 IO 的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。

信号驱动 IO

在使用信号驱动 I/O 时,当数据准备就绪后,内核通过发送一个 SIGIO 信号通知应用进程,应用进程就可以开始读取数据了。相比同步非阻塞 IO 来说,它不需要线程不断去轮询数据是否已经准备就绪

注意在"数据由内核空间拷贝到用户空间"阶段,它仍然是"阻塞"的。

异步 IO

用户线程发起 read 调用的同时注册一个回调函数,read 立即返回,等内核将数据准备好后,再调用指定的回调函数完成处理。在这个过程中,用户线程一直没有阻塞。

AIO最重要的一点是 从内核缓冲区拷贝数据到用户态缓冲区的过程也是由系统异步完成,应用进程只需要在指定的数组中引用数据即可。

AIO 与信号驱动 I/O 的主要区别:信号驱动 I/O 由内核通知何时可以开始一个 I/O 操作,而异步 I/O 由内核通知 I/O 操作何时已经完成。

AIO 是真正的异步模型,它不会对请求进程产生任何的阻塞。

小结

阻塞与非阻塞

日常使用过程中,我们往往把 同步I/O 等同于 阻塞I/O,异步I/O 等同于 非阻塞I/O,但严格意义来说,这两组概念还是有很大的区别的。

结合 I/O模型 来说,阻塞I/O 会一直 block 对应的进程直到操作完成,而 非阻塞IO 在"等待数据从网卡拷贝到内核"阶段会立刻返回。

所以我们一般认为,阻塞I/O 只有 BIO,另外四个模型都是属于 非阻塞I/O。

同步与异步

根据 POSIX 的定义:

  • 同步I/O : A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;
  • 异步I/O : An asynchronous I/O operation does not cause the requesting process to be blocked;

两者的区别就在于 同步I/O 做 "IO operation" 的时候会将 process 阻塞。

那么按照这个定义,BIO,NIO,IO多路复用、信号驱动IO 四种模型都属于 同步IO。因为它们在 IO 的第二阶段,真正执行"数据拷贝"的阶段,都是"阻塞"的。

相关推荐
东南门吹雪4 小时前
JAVA TCP socket编程框架
java·高并发·socket·tcp·nio
JackSparrow4141 天前
彻底理解Java NIO(三)Java实现 I/O多路复用+Reactor模式及开源框架代码解读
java·c语言·开发语言·后端·nio·reactor模式
布朗克1682 天前
25 IO流高级操作——序列化、NIO与Files工具类
java·数据库·io·nio
不懂的浪漫4 天前
10|Netty native epoll 与零拷贝:从 Java NIO 再往下看一层![
java·netty·nio
许彰午4 天前
24_Java NIO核心组件
java·python·nio
Irissgwe6 天前
11、五种 IO 模型与阻塞 IO
网络·阻塞·非阻塞·io模型·非阻塞io·异步通信·同步通信
径硕科技JINGdigital8 天前
GEO效果监测的四个核心维度:从提及率到信源分析的量化评估框架
aio·geo·aeo
径硕科技JINGdigital8 天前
B2B企业GEO监测的四个核心维度:从曝光计数到认知质量评估
aio·geo·aeo
C+++Python9 天前
如何在 Java 中使用 BIO、NIO 和 AIO?
java·开发语言·nio
Halo_tjn13 天前
NIO 技术的使用
java·开发语言·nio