, ,

IO多路复用

 为什么需要 IO 多路复用? 想象你是一个餐厅的厨师(CPU),同时负责做 100 桌客人的菜。 …

 为什么需要 IO 多路复用?

想象你是一个餐厅的厨师(CPU),同时负责做 100 桌客人的菜。

  • 阻塞 IO:你给第 1 桌客人下完单后,就站在传菜口死死等这桌菜做好。菜没做好,你什么都不干,也不去管其他 99 桌。这显然极其浪费你的时间(CPU 资源)。
  • 多线程/多进程:你雇了 100 个厨师,每人盯一桌。虽然解决了等待问题,但 100 个厨师的工资(内存开销)和互相沟通的成本(上下文切换开销)太高了。

IO 多路复用(I/O Multiplexing)就是完美的折中方案
你只需要一个人(单线程),站在一个“中央调度台”前。你不用死死盯着某一桌,而是让调度台帮你盯着这 100 桌。哪一桌的菜做好了(IO 就绪了),调度台就拍一下你的肩膀告诉你,你再去把那桌菜端走。这样,你一个人就能高效地处理成百上千个 IO 请求。

核心原理:从“轮询”到“事件通知”

IO 多路复用的本质,是操作系统提供的一种机制,允许一个进程/线程同时监控多个文件描述符(FD,如 Socket),并在某些 FD 可读或可写时,主动通知应用程序。

它的演进经历了三个重要阶段,完美诠释了原理的升级:

1. select:最早的“全员点名”

  • 原理:应用程序把所有需要监控的 FD 拷贝给内核。内核通过遍历所有 FD,检查它们是否就绪。
  • 痛点
    • 性能极差:每次都要把所有 FD 拷来拷去,且内核要线性扫描。
    • 数量限制:默认最多只能监控 1024 个 FD。

2. poll:取消了数量限制,但没解决效率问题

  • 原理:用链表代替了 select 的位图,打破了 1024 的限制。
  • 痛点:依然是线性扫描,FD 越多,内核检查越慢。

3. epoll(Linux 终极杀器):从“点名”到“订阅回调”

epoll 彻底改变了游戏规则,它是目前高性能网络服务器(如 Nginx、Redis)的基石。

  • 原理
    • 注册机制:应用程序通过 epoll_ctl 把需要监控的 FD 注册到内核的一个红黑树中(增删改的时间复杂度仅为 O(logN)O(logN) )。
    • 事件回调:当某个 FD 真正就绪时,内核通过回调函数将其加入一个“就绪链表(ready list)”。
    • 精准获取:应用程序调用 epoll_wait 时,内核直接把就绪链表返回,不需要任何遍历
  • 优势:不管监控 100 个还是 10 万个连接,只要活跃的只有 10 个,epoll_wait 就只返回这 10 个,效率极高。

📊 三种模型对比总结

表格

特性selectpollepoll
底层数据结构数组/位图链表红黑树 + 就绪链表
最大连接数1024无上限无上限(受系统内存限制)
FD 增加/删除开销O(N)O(N)O(N)O(N)O(logN)O(logN)
查询就绪状态线性遍历 O(N)O(N)线性遍历 O(N)O(N)回调机制 O(1)O(1)
适用场景连接数少且活跃逐渐被淘汰海量并发、高吞吐场景
  • Nginx:为什么单机能抗住几万并发?因为它底层使用了 epoll,一个 Worker 进程就能处理成千上万个连接。
  • Python asyncio / Node.js:它们底层的事件循环(Event Loop),在 Linux 上正是基于 epoll 实现的

一条对“IO多路复用”的回复

  1. 陈 某人 的头像

    select:通用轮询
    poll:基础事件轮询
    epoll:event poll(Linux)
    kqueue:kernel queue(macOS/BSD)
    IOCP:I/O Completion Ports(Windows)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

About the Author

每个人都有自己得时区,在自己得时区里,一切都是准时的。

BlockSpare — News, Magazine and Blog Addons for (Gutenberg) Block Editor