直接回答:三者都是 IO 多路复用机制,让一个线程监视多个 fd。select:用位图传 fd 集合,有数量上限(通常 1024),每次调用把整个集合从用户态拷到内核并 O(n) 遍历,返回后还要应用层再遍历一遍。poll:改用链表结构,去掉了数量上限,但仍是每次全量拷贝 + O(n) 轮询。epoll:fd 集合只在内核注册一次(epoll_ctl),内核用红黑树管理、就绪队列返回结果,epoll_wait 只拿到就绪的 fd,事件多时也无需全量扫描。
展开解析:epoll 的关键优化是"状态维持在内核"和"回调驱动":fd 就绪时通过回调加入就绪链表,而不是每次调用重新检查全部。epoll 支持水平触发(LT,默认,只要没处理完就反复通知,编程简单)和边缘触发(ET,只在状态变化瞬间通知一次,必须配非阻塞 IO 并循环读到 EAGAIN,效率更高但易错)。select/poll 的优势仅剩可移植性和连接数极小时的简单性。追问方向:ET 为什么要配非阻塞 fd、epoll 在大并发下 O(1) 的表述是否准确(返回就绪数而非总数)、kqueue/IOCP 的对应关系。