卡码笔记-最强八股文
首页
计算机基础
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
简历专栏
代码随想录 (opens new window)
首页
计算机基础
C++
Java
Go
🔥大模型🔥
  • 大模型面经
  • Java面经
  • C++面经
简历专栏
代码随想录 (opens new window)
  • 本栏必读

    • 关于本专栏
    • C++学习路线
    • C++面试题系优化
  • 基础与语法

  • 面向对象

  • STL 与容器

  • 内存管理

  • C++11 与现代 C++

  • 智能指针

  • 并发与 I/O

    • 互斥锁与自旋锁
    • 说一下select,poll和epoll
      • 简要回答
      • 详细回答
      • 知识拓展

# select、poll、epoll 的区别

面试官问"说一下 select、poll、epoll 的区别",大多数人能答出"epoll 性能最好",但追问"为什么好""好在哪""什么场景下 select 反而更合适",就开始含糊了。

这道题考的是你对内核 IO 多路复用实现机制的理解深度。把"数据结构""通知方式""内核态到用户态的数据拷贝"三个维度讲清楚,面试官就不会继续追了。

# 简要回答

三者都是让单线程同时监视多个 fd 就绪状态的机制。核心区别在于:select 用位图、有 1024 限制、每次轮询 O(n);poll 用数组、去掉数量限制、但仍 O(n) 遍历;epoll 用红黑树 + 就绪链表 + 事件回调,只返回活跃 fd,大量连接时性能碾压前两者。

# 详细回答

维度 select poll epoll
数据结构 位图 fd_set pollfd 结构体数组 红黑树 + 就绪链表
fd 数量限制 默认 1024(FD_SETSIZE) 无硬性限制(受系统 ulimit 约束) 无硬性限制
每次调用开销 重设 fd_set + 拷贝到内核 + 遍历所有 fd 拷贝整个数组到内核 + 遍历所有 fd 只取就绪链表,无需遍历
就绪通知方式 返回后需遍历所有 fd 找就绪的 返回后需遍历所有 fd 找就绪的 epoll_wait 直接返回就绪 fd 列表
时间复杂度 O(n) O(n) O(活跃连接数)
触发模式 仅水平触发(LT) 仅水平触发(LT) 支持 LT 和边缘触发(ET)
跨平台 是(POSIX) 是(POSIX) 否(Linux 特有)

为什么 epoll 在大量连接时碾压 select/poll

select/poll 每次调用都要把所有监控的 fd 从用户态拷贝到内核态,内核遍历所有 fd 检查就绪状态,返回后用户态再遍历一遍找到就绪的。连接数 1 万时,即使只有 10 个活跃,也要遍历 1 万次——这就是 O(n) 的代价。

epoll 的设计完全不同:epoll_ctl 注册 fd 时就把它挂到内核的红黑树上,之后不需要重复拷贝。当 fd 就绪时,内核通过回调把它加入就绪链表。epoll_wait 只需要检查就绪链表是否为空,非空就直接返回里面的 fd——不管总共监控了多少连接,开销只和活跃连接数成正比。

LT 和 ET 的区别

水平触发(LT,默认):只要 fd 处于就绪状态,epoll_wait 每次都会返回它。编程简单,但如果不及时处理会反复通知。边缘触发(ET):只在 fd 状态变化时通知一次,之后不再通知。必须一次性读完所有数据(用非阻塞 IO + 循环读到 EAGAIN),否则数据会丢。ET 减少了 epoll_wait 的返回次数,高并发场景性能更好,但编程复杂度更高。

select poll epoll对比

# 知识拓展

Q:连接数少的时候用哪个?

连接数在几十到几百时,三者性能差异不大。如果需要跨平台(macOS/Windows),只能用 select/poll(epoll 是 Linux 特有的,macOS 用 kqueue,Windows 用 IOCP)。连接数少 + 跨平台需求 → select/poll;Linux + 高并发 → epoll。

Q:epoll 的 mmap 是怎么回事?

早期说法是 epoll 用 mmap 让内核和用户态共享内存来避免拷贝。实际上现代 Linux 内核实现中,epoll_wait 返回就绪事件时仍然有一次从内核到用户态的拷贝(copy_to_user),但因为只拷贝就绪的 fd(而非全部),数据量很小,开销可以忽略。真正省的是"注册时一次性拷贝 vs 每次调用都拷贝"。

Q:select 的 1024 限制能改吗?

可以修改 FD_SETSIZE 宏重新编译,但不推荐。因为 fd_set 是固定大小的位图,改大了栈空间开销也大。需要大量 fd 时直接用 poll 或 epoll。

Q:ET 模式下为什么必须用非阻塞 IO?

因为 ET 只通知一次,你必须循环读到 EAGAIN 才能确保数据读完。如果 fd 是阻塞的,最后一次 read 会卡住(没数据了但还在等),整个事件循环就死了。所以 ET + 非阻塞 IO 是固定搭配。

Last Updated: 5/28/2026, 4:45:41 PM

← 互斥锁与自旋锁

评论

验证登录状态...

侧边栏 侧边栏
夜间模式 夜间
卡码简历 卡码简历
代码随想录 代码随想录
卡码投递表 卡码投递表🔥
2026实习校招群 2026群
添加客服微信 2026实习校招客服微信 PS:通过微信后,请发送姓名-学校-年级-2026实习/校招
支持卡码笔记 支持卡码笔记
鼓励/支持/赞赏Carl 卡码笔记赞赏码
1. 如果感觉本站对你很有帮助,也可以请Carl喝杯奶茶,金额大小不重要,心意已经收下
2. 希望大家都能梦想成真,有好的前程,加油💪