当前位置: 首页 > ds >正文

什么是IO多路复用

我们用一个非常生动的例子来解释IO多路复用。

想象一下你开了一家奶茶店,你的工作就是接收顾客的订单,然后让后厨去做,做完后把奶茶递给顾客。


第一种模式:同步阻塞IO(BIO - 最原始的模式)

你雇了一个服务员。这个服务员的工作流程是这样的:

  1. 站在柜台前,等一个顾客(A)来点单。

  2. A点单,服务员把单子送到后厨,然后就死死地站在后厨门口等,什么都不干,直到后厨把A的奶茶做好。

  3. 服务员把奶茶递给A。

  4. 然后,他才去接待下一个顾客(B),重复上述“等待-阻塞-递交”的过程。

问题: 效率极低!当服务员在等后厨做A的奶茶时,柜台前可能已经来了B、C、D三个顾客,他们都得干等着。这个服务员绝大部分时间都“阻塞”在了后厨门口。


第二种模式:IO多路复用(NIO - 高效的老板模式)

你觉得这样太浪费人力了,于是你自己(内核) 来当老板,并且买了一个 “后厨完工提醒铃”(操作系统提供的Selector/Epoll机制)

现在你的工作流程是这样的:

  1. 你给每个顾客一个号码牌,并告诉他们:“单子我收下了,奶茶做好了我会按你的号码牌响铃,你听到再来取”。

  2. 你不再傻等。你拿一个小本本(文件描述符集合FD Set),把所有下了单但还没拿到奶茶的顾客号码都记下来。

  3. 然后你就去做别的事情(比如算账、打扫),或者干脆休息。你不需要关心后厨是怎么做的。

  4. 突然,“叮!”的一声,提醒铃响了,告诉你“5号顾客的奶茶好了!”。

  5. 你立刻去查看你的小本本,发现5号是顾客E。于是你大声喊:“E同学,你的奶茶好了,来取一下!”

  6. 在E来取奶茶的间隙,提醒铃又响了,告诉你“3号好了”。你一看小本本,3号是顾客C,你又喊:“C同学,你的好了!”

  7. 整个过程,你(一个线程/进程) 同时监听和管理着几十个顾客的订单状态。你只在“提醒铃响”(IO事件就绪)这个瞬间才进行响应,其他时间都是非阻塞的。


例子总结:

角色比喻真实含义
你(老板)一个线程/进程处理业务的核心
顾客客户端连接(Client Connection)来自网络的各种请求
点单连接请求/数据请求read/write 等IO操作
后厨做奶茶内核准备数据数据从网络/磁盘读取到内核缓冲区
递交奶茶数据从内核空间拷贝到用户空间应用程序真正处理数据
一个服务员一个线程传统BIO模型的资源消耗
号码牌文件描述符(File Descriptor, FD)操作系统对每个连接的标识
小本本文件描述符集合(FD Set)你监控的所有连接
提醒铃内核通知机制(select/poll/epoll)IO多路复用的核心

IO多路复用的核心思想:

用一个进程(你)来监视多个文件描述符(顾客连接)上的事件(奶茶好了)。一旦某个描述符就绪(事件发生),内核就通知应用程序,应用程序再对该描述符进行相应的IO操作。

这种模式的巨大优势在于:用很少的资源(一个老板)就能管理海量的连接(成千上万的顾客)。如果一个连接用一个服务员(线程),成百上千个连接就会产生成百上千个线程,线程的上下文切换开销会压垮系统。而IO多路复用只需要一个或几个线程,极大地提升了系统的性能和可扩展性。

常见的IO多路复用技术有:selectpollepoll(Linux),kqueue(BSD/Mac)等。其中epoll就是那个更智能、更高效的“提醒铃”,它能准确地告诉你具体是哪个“号码牌”响了,而不需要你一个个去问。

我们继续用奶茶店的例子来解释 ET(Edge-Triggered,边缘触发) 和 LT(Level-Triggered,水平触发) 模式的区别。

这是IO多路复用中非常关键的概念,尤其是对于高效的 epoll 模型。


场景回顾:你的奶茶店和“提醒铃”系统

你作为老板,已经实现了IO多路复用。你的“提醒铃”响了,代表后厨有奶茶做好了。但是,这个“提醒铃”有两种不同的工作模式:


1. LT模式 (水平触发模式) - “贴心小秘书”

工作方式: 只要后厨的出货台上还有做好的奶茶没被取走,这个铃就会一直响、隔一会儿就响一次,不停地提醒你:“老板!这儿还有货没取呢!别忘了啊!”

工作流程:

  1. 铃响了,告诉你“5号奶茶好了”。

  2. 你喊:“5号,来取奶茶!”。

  3. 假设5号顾客是个磨蹭鬼,他一直没来取。

  4. 你的铃会不断地再次响起:“5号好了!”、“5号好了!”、“5号好了!”。直到5号顾客终于把奶茶取走,出货台空了,铃才停止提醒。

LT模式的特点:

  • 只要条件满足(奶茶还在台上),就会持续通知。

  • 非常贴心,你几乎不可能忘记处理某个就绪的事件。

  • 即使你某次因为忙别的没处理(比如没喊顾客),铃还会再次提醒你。

  • 编程模型更简单,不容易出错。


2. ET模式 (边缘触发模式) - “高冷一次性通知”

工作方式: 只有当后厨新做好一杯奶茶并放到出货台的那一刻,这个铃会响一声,而且只响一声。之后哪怕奶茶一直放在那里没人取,它也绝不会再响第二次。

工作流程:

  1. 铃响了一声,告诉你“5号奶茶好了”(这是从“没好”到“好了”的状态变化)。

  2. 必须立刻、马上处理这件事。因为铃只会告诉你这一次。

  3. 你喊:“5号,来取奶茶!”。

  4. 如果5号顾客没来取,奶茶就一直放在那里。但你的铃永远沉默了,再也不会提醒你5号奶茶还在那儿。

  5. 如果之后后厨又新做了一杯7号奶茶,铃会在7号做好的那一刻再响一声(一次新的状态变化),但依然不会管台上还放着的5号奶茶。

ET模式的特点:

  • 只在状态发生变化时(从“未就绪”变为“就绪”)通知一次。

  • 非常高效,减少了重复通知的开销。

  • 要求使用者(程序员)必须一次性把数据读完/写完。因为如果你这次没处理完,下次就不会再通知你了,那些数据就会永远“沉睡”在内核缓冲区里。

  • 编程难度更高,容易遗漏事件,导致bug。


核心区别与如何选择

特性LT模式 (水平触发)ET模式 (边缘触发)
通知频率只要条件满足,反复通知只在状态变化时,通知一次
编程难度简单,不易出错复杂,容易遗漏事件
性能可能有重复通知的微小开销更高,通知次数更少
工作方式像一个唠叨的秘书像一个高冷的信使
操作要求可以多次读取,这次没读完下次还会提醒必须一次读完所有数据,直到出错(EAGAIN/EWOULDBLOCK

程序员应该怎么做?

  • 如果用LT模式:铃响了,你知道有数据可读。你可以读一点,然后去处理别的连接。下次铃再响时,你继续读一点。虽然效率不是最高,但很安全。

  • 如果用ET模式:铃响了一声,你知道有新的数据来了。你必须立刻创建一个循环,疯狂地读这个连接的数据,直到read系统调用返回错误(EAGAIN),表示内核缓冲区里暂时没数据了,你才敢停下来。如果你只读了一次,而数据还有剩余,那么这些剩余数据就再也没人知道了。

结论

  • LT是默认模式,更符合人的直觉,更好用。

  • ET是高性能模式,但需要程序员非常小心,编写更复杂的代码来保证正确处理所有数据。很多高性能网络库(如Nginx)使用ET模式来榨干系统性能。

简单说,LT是“有数据就催你处理”,ET是“来新数据了,我只说一遍,处理不完你看着办”

http://www.xdnf.cn/news/19353.html

相关文章:

  • ESPTimer vs GPTimer:ESP32 定时器系统深度解析
  • 【Java基础知识 19】继承
  • Spring注解演进与自动装配原理深度解析:从历史发展到自定义Starter实践
  • 197-200CSS3响应式布局,BFC
  • 内存管理(智能指针,内存对齐,野指针,悬空指针)
  • 时间轴组件开发:实现灵活的时间范围选择
  • PHP单独使用phinx使用数据库迁移
  • Spring Cloud微服务架构设计与实战:从组件落地到分布式事务解决
  • 精简版UDP网络编程:Socket套接字应用
  • 链表有环找入口节点原理
  • css绘制三角形
  • A股大盘数据-20250829 分析
  • C++基础(③反转字符串(字符串 + 双指针))
  • 阿里巴巴拍立淘API返回值解析与商品信息优化指南
  • 刷题日记0829
  • Libvio 访问异常排查指南
  • OpenEuler部署LoganaLyzer
  • linux实时性研究
  • Python 编码与加密全解析:从字符编码到 RSA 签名验证
  • Win11 压缩实测:Win11 的压缩软件的最佳配置和使用方式
  • 龙迅#LT7621GX适用于两路HDMI2.1/DP1.4A转HDMI2.1混切应用,分辨率高达8K60HZ!
  • Anaconda安装与conda使用详细版
  • Linux系统编程—进程概念
  • 文本嵌入模型的本质
  • 进程与线程的根本区别
  • Parasoft赋能测试:精准捕捉运行时缺陷
  • 解决RTX3070魔改16G在UBUNTU中黑屏问题
  • AI ToB,阿里商旅找了个好赛道
  • C++ 并发编程:全面解析主流锁管理类
  • Day17_【机器学习—特征预处理(归一化和标准化)】