高 困难
select-poll-epoll对比#
一句话答案#
select 有 1024 fd 限制且全量遍历,poll 无限制但仍全量,epoll 红黑树+回调链表 O(1) 就绪检测最高效。
核心要点
| 维度 | select | poll | epoll |
|---|---|---|---|
| fd上限 | 1024 | 无限制 | 无限制 |
| fd传递 | 每次全量拷贝 | 每次全量 | epoll_ctl增量 |
| 就绪检测 | O(n)遍历 | O(n)遍历 | O(1)就绪链表 |
| 触发方式 | 水平触发 | 水平触发 | ET+LT |
结论: 大量连接用epoll,少量连接差异不大
面试回答(2分钟版)
select、poll 和 epoll 都是 IO 多路复用的实现方式,核心思想是一个线程同时监控多个文件描述符的就绪状态。select 是最早的实现,它用 fd_set 位图存储要监控的 fd,调用时需要把整个 fd_set 从用户态拷贝到内核态,内核遍历所有 fd 检查就绪状态再拷贝回来,时间复杂度 O(n),而且 fd_set 大小被 FD_SETSIZE 限制为 1024。poll 用 pollfd 数组替代位图去掉了 1024 的限制,但仍然需要每次全量拷贝和 O(n) 遍历。epoll 做了根本性的改进:通过 epoll_create 创建一个内核事件表,用红黑树存储监控的 fd,epoll_ctl 做增量注册而不是每次全量拷贝。当 fd 就绪时通过回调机制将其加入就绪链表,epoll_wait 只需要检查就绪链表即可,时间复杂度 O(1)。epoll 还支持边缘触发 ET 模式,只在状态变化时通知一次,比默认的水平触发 LT 减少了事件通知次数,但编程更复杂需要循环读取直到 EAGAIN。Redis 和 Nginx 在 Linux 上都使用 epoll 实现高并发连接处理。
追问与易错
追问方向:
- “epoll 的 LT 和 ET 什么区别?”→ LT(水平触发)只要 fd 就绪就一直通知,ET(边缘触发)只在状态变化时通知一次,ET 减少了通知次数但要求一次读完所有数据
- “连接数少时用哪个?”→ 连接数少(几十个)时 select/poll 足够且实现简单,epoll 在大量连接但活跃连接少的场景优势最明显
- “为什么 ET 编程更复杂?”→ ET 只通知一次,必须循环读取直到返回 EAGAIN 否则数据会丢失,且必须使用非阻塞 IO 防止阻塞在最后一次 read 上
易错点:
- ❌ epoll 永远最好——连接数少时 select 更简单
- ❌ ET 一定比 LT 快——ET 需要循环读取更复杂