epoll 是 Linux 内核为处理大批量文件描述符而作了改进的 I/O 多路复用 机制。它通过红黑树管理监控对象,通过事件回调通知就绪状态,彻底解决了 select/poll 在高并发下的 性能瓶颈。

一、 核心架构

epoll 的高效源于其在内核中维护的两个核心数据结构:

组件数据结构作用优势
监控列表红黑树 (RB-Tree)存储所有被监控的 FD增删改查复杂度 ,避免全量拷贝
就绪队列双向链表 (Ready List)存放已触发事件的 FDepoll_wait 只需 检查链表是否为空

二、 工作流程

epoll 采用“被动通知”机制,其核心流程如下:

sequenceDiagram
    participant U as 用户进程
    participant K as 内核 (Kernel)
    participant RB as 红黑树 (RB-Tree)
    participant RL as 就绪链表 (Ready List)

    Note over U, RL: 1. 设置阶段
    U->>K: epoll_create()
    K->>K: 创建 epoll 实例 (RB + RL)

    rect rgb(240, 240, 240)
    Note over K, RL: 异步触发过程 (从注册起持续运行)
    U->>K: epoll_ctl(ADD, fd, event)
    K->>RB: 插入节点
    K-->>K: 注册回调函数 (Callback) 到设备驱动

    Note right of K: 硬件中断 / 数据到达
    K->>RL: 触发回调,将 FD 移入 RL

    Note over U, RL: 2. 等待与处理阶段
    U->>K: epoll_wait()
    K-->>K: 检查 RL 是否为空 (若空则阻塞)
    K->>U: 返回就绪 FD 数量
    U->>U: 仅遍历 RL 中的就绪 FD
    end

三、 触发模式对比

模式核心逻辑适用场景编程复杂度
LT (水平触发)只要缓冲区有数据,就持续通知默认模式,容错性高简单
ET (边缘触发)仅在状态改变时通知一次高性能服务器 (如 Nginx)高 (需配合非阻塞 I/O)

四、 为什么 epoll 更快?

  1. 减少拷贝epoll_ctl 每次只操作一个 FD,不需要像 select 每次调用都拷贝整个 FD 集合。
  2. 减少遍历:内核通过回调机制直接填充就绪链表,无需像 poll 那样线性扫描所有 FD。
  3. 无上限限制:监控的 FD 数量仅受系统内存限制。