epoll 和 select 都是 Linux 下 I/O 多路复用机制(I/O multiplexing) 的实现方式,用来高效监听多个文件描述符(socket、文件等)的 I/O 事件(如可读、可写、异常)。
1. 什么是 select?
select() 是最早期的 I/O 多路复用接口,定义在 <sys/select.h> 中。
特点:
- 接口:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
- 核心机制:用户将要监听的文件描述符集合传入内核,内核遍历每个 fd 的状态,将结果写回到用户空间。
- 监听 fd 数有限制:1024(默认),可通过 FD_SETSIZE 修改。
- 每次调用都要复制 fd 集合(开销大)并重新设置监听集合。
- 内核每次都要 线性遍历 所有 fd(O(n))。
2. 什么是 epoll?
epoll 是 Linux 2.6 内核引入的新接口,解决了 select/poll 的性能问题。
特点:
- 包括三个系统调用:
- epoll_create():创建 epoll 实例
- epoll_ctl():注册、修改、删除监听的 fd
- epoll_wait():阻塞等待事件发生
- 不限制监听 fd 的数量(只受系统 ulimit 限制)
- 内核内部维护事件列表(红黑树 + 就绪队列),避免每次复制大量 fd。
- 使用回调机制通知事件(高效),无需每次遍历所有 fd(性能 O(1))
3. select 与 epoll 的关系
它们都是 Linux 提供的 I/O 多路复用 API,用于处理高并发连接的网络程序(如 Nginx、Redis、Kafka、Go/Rust 网络库)。
对比项 | select | epoll |
引入时间 | 早期 Unix/Linux | Linux 2.6+ |
fd 数限制 | 有(默认 1024) | 无限制(受 ulimit 限制) |
内核数据结构 | 数组 + 位图 | 红黑树(管理)+ 链表(事件队列) |
性能 | O(n) | O(1)(事件触发) |
事件通知方式 | 轮询(每次遍历) | 回调(只返回活跃 fd) |
是否支持边缘触发 | 否 | 支持 ET(Edge Triggered) |
是否跨平台 | 是(POSIX 标准) | 否(Linux 特有) |
4. 举例:谁在用 epoll?
- Nginx:使用 epoll 高效处理成千上万并发连接。
- Redis:Linux 下优先使用 epoll。
- libevent、libev、tokio、Go runtime:都会在 Linux 下优先使用 epoll。
5. 总结一句话:
select 是老旧但广泛支持的接口,epoll 是 Linux 下专为高性能而生的多路复用机制,二者本质都是为了解决“一个线程/进程同时监听多个 I/O”的问题。
如果你是做高性能网络编程或系统服务,在 Linux 上应该优先使用 epoll。