Linux Socket多路复用本质是单线程高效监控多个fd;select跨平台但受限于1024上限、每次全量拷贝与遍历,效率低。

Linux Socket 多路复用机制本质是让单个线程高效监控多个 socket(即文件描述符 fd),避免为每个连接开一个线程,大幅降低系统开销。select、poll、epoll 是三种主流实现,它们解决的是同一个问题,但路径和效率差异明显。
select:跨平台但受限的老将
select 是 POSIX 标准接口,几乎所有 Unix/Linux 系统都支持,最大优势是兼容性好。它用 fd_set 位图管理待监听的 fd,最多支持 1024 个(FD_SETSIZE 默认值),硬编码限制无法绕过。
每次调用需做三件事:把整个 fd_set 从用户态拷贝进内核、内核线性遍历所有 fd 检查就绪状态、再把修改后的 fd_set 拷贝回用户态。返回后,你还得自己遍历全部 fd,靠 FD_ISSET() 逐个判断哪些真正就绪——哪怕只有一个 fd 就绪,也要扫完全部。
- 必须每次调用前重置 fd_set(内核会清空非就绪位)
- 超时参数 timeval 可设为 NULL(永久阻塞)或指定秒/微秒
- 适合 fd 数少(
poll:突破数量限制,仍逃不开轮询
poll 用 pollfd 结构体数组替代位图,不再受 1024 限制,只取决于内存大小。接口更清晰:一个数组就能同时传入读、写、异常事件标志,不用像 select 那样拆成三个集合。
但它没改掉核心缺陷——每次调用仍要全量拷贝整个 pollfd 数组到内核,并由内核线性扫描每一个元素。fd 越多,拷贝和遍历开销越大,时间复杂度稳定为 O(n)。
- pollfd 中 events 字段声明关注哪些事件,revents 字段由内核填入实际就绪事件
- 没有“最大 fd + 1”这类额外参数,使用比 select 略简洁
- 适用于 fd 数中等(几百)、但需要突破 1024 限制,又暂不依赖 epoll 特性的场景
epoll:Linux 高并发的首选方案
epoll 是 Linux 2.6 内核引入的改进方案,核心思路是事件驱动 + 红黑树 + 就绪链表。它把 fd 管理和事件通知彻底分离:先用 epoll_ctl() 注册/修改/删除 fd(存入红黑树),再用 epoll_wait() 等待就绪事件——内核只返回真正就绪的 fd 列表,无需遍历全部。
数据结构升级带来质变:注册一次长期有效(除非显式删除),内核在 fd 就绪时直接将其加入就绪链表;epoll_wait 只拷贝就绪项,不是全部 fd,时间复杂度接近 O(1)(就绪数)而非 O(n)(总 fd 数)。
- 支持两种触发模式:LT(水平触发,默认,只要就绪就持续通知)和 ET(边缘触发,仅状态变化时通知一次,要求一次性读完)
- epoll_create() 创建句柄,epoll_ctl() 管理 fd,epoll_wait() 获取就绪事件
- 适用于高并发服务(如 Web 服务器、消息中间件),fd 数常达数千甚至数万
怎么选?看实际约束
不是越新越好,而是看环境与规模:
- 要跑在 macOS 或老嵌入式系统上 → 选 select
- fd 数几百、不想改代码适配 epoll、也不追求极致性能 → poll 更省心
- Linux 服务器、fd 经常上千、追求吞吐和响应延迟 → epoll 是事实标准
- 现代框架(如 nginx、redis)默认用 epoll;libevent/libev 底层也会根据系统自动选型


















