I/O 多路复用让一个线程等待多个文件描述符的就绪事件。它没有让一次读写变快,而是避免为每个连接都安排一个阻塞线程,适合连接多、单连接事件稀疏的网络服务。

select 每次传入描述符集合,返回后应用扫描集合找就绪项,集合大小受实现限制且需反复复制;poll 用数组描述事件,去掉固定编号上限,但仍要线性扫描。Linux epoll 把关注集合注册在内核,等待时主要返回已就绪事件,避免每轮重传完整集合,更适合大量连接。

事件触发有两种语义。水平触发只要仍可读写就会继续通知,容易编写;边缘触发只在状态变化时提醒,应用通常必须把非阻塞描述符循环读到 EAGAIN,否则剩余数据可能长时间得不到新通知。就绪不等于业务消息完整,TCP 仍是字节流,需要应用层处理半包与粘连。

从接口观察,select/poll 每轮工作与监控数量相关,epoll 等待返回成本更接近就绪数量,但注册、回调、内核实现与场景都会影响实际性能,小规模下差异未必重要。

误区是把 epoll 说成完全 O(1)、认为就绪后读写绝不阻塞、边缘触发只读一次、关闭描述符后仍复用旧事件,以及把多路复用等同异步 I/O。小结:多路复用解决“等谁”的问题,非阻塞读写解决“处理时别卡住”,协议状态机解决“数据是否完整”。