select/poll/epoll到底是什么一回事

本文详细介绍了select、poll和epoll在Linux网络编程中的作用,阐述了它们的区别和优缺点。重点讨论了epoll的epoll_create()、epoll_ctl()和epoll_wait()三个核心函数,解释了epoll如何通过红黑树和就绪链表提高效率,以及为什么epoll比select和poll更高效。

面试题:说说select/poll/epoll的区别。
这是面试后台开发时的高频面试题,属于网络编程和IO那一块的知识。Android里面的Handler消息处理机制的底层实现就用到了epoll。
为此,我在Google上看了很多相关文章,才大概搞懂是怎么一回事。

背景知识

文件描述符fd

文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。

文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。在Linux系统中,流在内核中可以表示成文件的形式。

IO模型

IO可以理解成对流的操作。

一般对于一个read操作发生时,它会经历两个阶段。

  • 第一个阶段是等待数据准备。
  • 第二个阶段是真正读取的过程,将数据从内核缓冲区拷贝到用户进程缓冲区中,

而五种常见的IO模型也是围绕这两个阶段来区分的。

  • 同步模型(synchronous IO)
    • 阻塞IO(bloking IO)
    • 非阻塞IO(non-blocking IO)
    • 多路复用IO(multiplexing IO)
    • 信号驱动式IO(signal-driven IO)
  • 异步IO(asynchronous IO)

其中,IO多路复用就是一种机制,实现一个进程可以监视多个描述符,一旦某个描述符就绪,就能够通知程序进

### 定义及概述 #### Select 模型 `select` 是一种早期的 I/O 多路复用机制,支持跨平台使用。它通过单一系统调用监控多个文件描述符的状态变化,能够检测是否有数据可读、是否可以写入以及是否存在异常条件[^1]。 `select` 的核心功能在于等待一组文件描述符中的任何一个变为就绪状态。 #### Poll 模型 `poll` 是对 `select` 的改进版本,在某些方面解决了其局限性。与 `select` 类似,`poll` 同样用于监视多个文件描述符的 I/O 就绪情况,但它没有文件描述符数量的限制(不像 `select` 受制于 FD_SETSIZE)。然而,两者的工作方式本质上相似,都需要遍历整个文件描述符集合以查找就绪的描述符[^2]。 #### Epoll 模型 `epoll` 是 Linux 独有的高效 I/O 复用接口,专为高并发场景设计。相比 `select` 和 `poll`,`epoll` 提供了一种更高效的件通知机制。它的实现依赖三个主要函数:`epoll_create` 用于创建 epoll 实例,`epoll_ctl` 负责注册或移除感兴趣的文件描述符及其对应的件类型,而 `epoll_wait` 则阻塞当前线程直到有件发生并返回这些件列表[^4]。 --- ### 工作原理对比 #### Select 的工作原理 `select` 维护了一个位图结构来表示要监听的所有文件描述符,并将其传递给内核。每次调用时,都会将用户空间的数据复制到内核空间进行处理,之后再把结果复制回用户空间。这种全量复制操作带来了较高的性能开销,尤其是在大量文件描述符的情况下。 #### Poll 的工作原理 尽管 `poll` 解除了 `select` 中关于最大文件描述符数目的限制,但其实现仍然存在类似的缺点——即每次调用都需向内核提交完整的文件描述符集以便轮询检查哪些已准备好执行 I/O 操作。因此当面对成千上万连接时效率低下[^2]。 #### Epoll 的工作原理 不同于前两者的被动轮询模式,`epoll` 借助回调机制主动告知应用程序那些发生了特定类型的活动的文件句柄们。具体来说: - **epoll_create**: 初始化一个新的 epoll 对象; - **epoll_ctl**: 动态增删关注的目标 fd 或修改它们关联的兴趣点; - **epoll_wait**: 获取一批已经满足条件的通知项而非逐一查询每一个可能的对象[^3][^4]。 由于采用了基于件驱动的设计理念加上减少了不必要的上下文切换次数等因素的影响使得该方法非常适合现代互联网服务端架构下的海量短连接请求环境应用需求分析评估报告结论总结如下: --- ### 主要区别 | 特性 | Select | Poll | Epoll | |--------------------|----------------------------------|--------------------------------|-------------------------------| | 文件描述符限制 | 存在固定上限 | 无明确限制 | 无 | | 性能瓶颈 | O(n)复杂度 | O(n)复杂度 | O(1),仅针对活跃连接 | | 内存消耗 | 用户态<->内核态频繁数据交换 | 较少减少部分重复计算 | 极低,只有初次添加时涉及拷贝 | | 平台兼容性 | 跨平台 | 跨平台 | 限于Linux | --- ### 示例代码展示 以下是三种模型简单使用的 Python 示例代码片段: #### Select 示例 ```python import select import socket socks = [socket.socket() for _ in range(5)] readable, _, _ = select.select(socks, [], []) for s in readable: data = s.recv(1024) ``` #### Poll 示例 ```python import select import socket poller = select.poll() sock = socket.socket() poller.register(sock.fileno(), select.POLLIN) events = poller.poll(timeout=1000) for fileno, event in events: if event & select.POLLIN: sock.recv(1024) ``` #### Epoll 示例 ```python import select import socket epoller = select.epoll() sock = socket.socket() epoller.register(sock.fileno(), select.EPOLLIN) events = epoller.poll(timeout=1.0) for fileno, event in events: if event & select.EPOLLIN: sock.recv(1024) ``` --- ### 结论 综上所述,虽然 `select` 和 `poll` 在一定程度上实现了多路复用的功能,但由于其固有的缺陷并不适合大规模网络服务器的应用场合;相比之下,`epoll` 凭借着优秀的扩展性和卓越的表现成为当今主流的选择之一[^5]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值