五种 IO 模型与它们背后的消耗

五种 IO 模型与它们背后的消耗

五种 IO 模型与它们背后的消耗不同的 IO 模型之间有什么区别?不同类型的 IO 方式之间,有哪些区别,我们应该怎么根据业务逻辑选择合适的 IO 模型?

前言服务的 IO 直接影响着系统吞吐量和资源利用率,很多时候比起业务逻辑,会消耗更多的时间与 CPU。这要求我们深入理解不同种类 IO 及其背后原理,尽量规避不必要的消耗,选择更加高效合适的 IO 模型。幸运的是不同类型的 IO 一般已经被框架封装的很好了,作为业务程序员我们需要了解框架提供给我们的接口属于哪种 IO,对于 IO 上的消耗有一定的预期,然后就可以聚焦于业务逻辑的实现了。

这篇文章主要重点在不同种类的 IO 之间的区别,各自的线程行为及其低效或高效的原因,这部分消耗来源于何处,以及每种 IO 对应于我们实际使用过程中的例子。

IO 操作(如远程调用、文件读写等)在 Linux 操作系统中被抽象为文件描述符(fd)的统一处理。

总览

阻塞 IO最简单的 IO 模型。当前线程会阻塞自己,等待 IO 系统调用完成,操作系统内核中的数据已经就绪可被使用时,当前线程再被唤醒。

线程行为:

线程塞直至 IO 操作完成单次 IO 对应单一线程阻塞资源开销:

操作系统级的上下文切换线程状态会变为等待态数据就绪后需要唤醒线程数据由线程进行读写,数据在内核空间与用户空间之间复制例子:

同步的 rpc 调用读取文件这是最简单,最容易实现的 IO 方式,同步阻塞特性在需要立即获取结果的场景中仍有不可替代性,但会带来额外的消耗,这在高并发,高吞吐量的场景下更为严重

非阻塞 IO(非阻塞 socket)这个名字其实有些笼统,除了阻塞 IO,其他种类 IO 都是非阻塞 IO。这里特指非阻塞模式 socket。

当 socket 被设置为非阻塞模式时,进行系统调用接收数据时立即返回,若内核缓冲区没有就绪数据,则返回错误,有数据就绪,则返回数据。

线程行为:

由程序逻辑自行控制线程是否等待如果进行轮询等待,则线程会阻轮询 IO 所需要的所有时间轮询间隔直接影响CPU利用率资源开销:

线程系统调用确认数据是否就绪,高频系统调用产生的上下文切换轮询,大量轮询造成的空转 CPU 消耗数据由线程进行读写,数据在内核空间与用户空间之间复制例子:

单线程轮询少量连接相较简单的阻塞 IO,这种方式需要我们通过轮询或者其他方法获取,但也给予了程序逻辑更多自由,可以自行选择是否在此轮询等待,或者进行其他逻辑。

如果是简单的轮询,会造成 CPU 的空转,持续占用 CPU 时间片,导致其他线程无法有效利用 CPU,甚至比阻塞 IO 还要差了。

IO 复用通过 epoll 等操作系统支持的系统调用,由一个线程监控多个 IO 请求。线程注册需要等待的 IO fd,再由一个线程为所有需要等待的 IO 线程阻塞等待,并在 fd 数据就绪后进行任务分配。即,让所有需要 IO 的线程复用同一个专门用来监控 fd 的线程。

线程行为:

发起 IO 的线程不等待,在将 fd 交予监控线程后,可以立即执行其他任务资源开销:

单次系统调用管理多个fd(O(1)时间复杂度)

仅监控线程产生阻塞开销

数据由线程进行读写,数据在内核空间与用户空间之间复制

例子:

epoll(Linux)kqueue(BSD)相较于前两种 IO 模型,IO 复用已经优化了太多了,一个监控线程可以负责多个 IO fd,由于 IO 产生的阻塞开销由 O(n) 下降到了 O(1),在高并发的场景下,单个线程的阻塞对于 CPU 的浪费完全可以忽略。

epoll,IO 多路复用模型是现代高并发系统的基石,无论是 Nginx 还是 Java NIO,底层 IO 模型都是 IO 复用。

信号驱动 IO应用通过系统调用设置信号驱动模式,注册信号处理函数,注册后立即返回。数据就绪后,内核会发送信号(SIGIO)通知应用。未指定线程时,内核会选择一个未阻塞该信号且处于可运行状态的线程来执行信号处理函数。(若通过F_SETOWN或信号掩码将信号绑定到某线程,该线程会成为信号处理函数的执行者)

信号通知(即数据就绪)是异步的,但数据复制的阶段仍需应用主动调用阻塞的读写操作(同步特性),因此信号驱动 IO 仍然算作同步 IO。

线程行为:

发起 IO 的线程无阻塞,注册信号处理函数后,可以立即执行其他任务由回调函数的执行者进行数据复制和后续处理,信号处理函数执行上下文不确定资源开销:

信号处理带来的上下文切换数据由线程进行读写,数据在内核空间与用户空间之间复制例子:

fcntl 设置信号驱动模式并注册处理函数(Linux)信号驱动 IO 带来很多复杂度(注册信号处理函数,选择回调的执行线程),相比较 IO 复用,减少的主动轮询开销在高并发场景下并不明显,而信号处理的异步特性和编程复杂度限制了其广泛应用。

异步 IO提交 IO 操作后立即返回,由操作系统进行全部 IO 内容,包括数据复制,完成后通知应用进程。

线程行为:

全程无任何形式的线程阻塞提交 IO 请求后无需其他操作资源开销:

消除用户态数据复制零阻塞的系统调用例子:

AIO(Linux)io_uring(Linux)IOCP(Windows)信号驱动 IO 只是由内核通知我们何时可以开始下一个 IO 操作,而异步 IO 模型是由内核通知我们 IO 操作什么时候已经完成。最小化上下文切换,适合高并发场景。

总结低效的 IO 方式产生更多浪费,高效的 IO 方式带来更高的复杂度和编码难度。

在我们的业务层级的代码中,特别是 IO 密集的服务,我们可以借鉴这些 IO 模型,有意识的选择合适的 IO 模型组织我们的逻辑,进而减少消耗,增大应用的吞吐量。

减少 IO 消耗的关键首先是避免阻塞,其次是避免多余的上下文切换。深入的了解 IO 模型,即使因为复杂度和实现问题选择简单的 IO 模型,也能了解资源浪费在哪里了,哪里还能挤出水来。

参考100%弄明白5种IO模型 - 知乎

[译] Linux 异步 I/O 框架 io_uring:基本原理、程序示例与性能压测(2020)

文档信息本文作者:nyaaar本文链接:https://nyaaarlathotep.github.io/2025/05/05/IO/版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)

相关推荐

阿根廷首战失利,他拥有了一切,现在就差一个世界杯冠军
frida学习--01:安装与简单使用
365网新闻

frida学习--01:安装与简单使用

📅 08-18 👁️ 1196
​啤酒放进冰箱多久才结冰(啤酒放冰箱多久可以喝)
英国beat365官方登录

​啤酒放进冰箱多久才结冰(啤酒放冰箱多久可以喝)

📅 07-17 👁️ 5721
部队工资待遇一览表 每月收入有多少
365网新闻

部队工资待遇一览表 每月收入有多少

📅 08-15 👁️ 473
正在阅读:谁更胜一筹?海美迪H7三代对比小米盒子谁更胜一筹?海美迪H7三代对比小米盒子
肖师傅空气滤芯质量怎么样
英国beat365官方登录

肖师傅空气滤芯质量怎么样

📅 07-25 👁️ 7469
在WPS Office Word中自定义自动编号样式的全面指南:个性化技巧与操作方法
中国健康快餐市场研究报告 - 健康快餐行业容量分析及预测
摄像头噪声产生原因及应对方法
英国beat365官方登录

摄像头噪声产生原因及应对方法

📅 09-17 👁️ 1842
为什么感觉现在全中国都在加班,到底都在加什么?现在的加班呢,愈发呈现出几种现象:第一个,加班最初的意思,是这一种非常规的...
拼多多机构入驻需要多久通过
中爱365APP

拼多多机构入驻需要多久通过

📅 08-03 👁️ 5350
TANGLE TEEZER
中爱365APP

TANGLE TEEZER

📅 08-29 👁️ 6462