土法炼钢兴趣小组的算法知识备份

【操作系统百科】epoll 内部

文章导航

分类入口
os
标签入口
#epoll#event-poll#lt-et#thundering-herd#ready-list

目录

epoll 系列阅读分工

篇目 路径 一句话
本篇(速览) OS 58 结构全景 + LT/ET/惊群要点,作复习索引
内核主文 57|epoll 数据结构 fs/eventpoll.c、回调、ovflistep_send_events
网络工程 67|epoll 深度剖析 ET/LT 编程范式、生产陷阱、timerfd 集成
io_uring 对比 epoll 与 io_uring 对比 就绪通知 vs 完成通知

推荐阅读路径本篇57 内核67 工程io_uring 对比

epoll 是 Linux 网络服务器的核心 I/O 多路复用机制。它为什么比 select/poll 快?内部怎么实现?下面用一图 + 要点速览;细节以算法 57 与网络 67 为准。

一、先看图

flowchart TD
    subgraph eventpoll
        RBTREE[红黑树<br/>管理所有 fd]
        RDLLIST[就绪链表<br/>有事件的 fd]
        WAIT[等待队列<br/>阻塞的 epoll_wait]
    end

    ADD[epoll_ctl ADD] --> RBTREE
    MOD[epoll_ctl MOD] --> RBTREE
    DEL[epoll_ctl DEL] --> RBTREE

    RBTREE -->|回调触发| RDLLIST
    RDLLIST --> EWAIT[epoll_wait<br/>返回就绪 fd]
    EWAIT --> WAIT

    classDef ep fill:#388bfd22,stroke:#388bfd,color:#adbac7;
    classDef op fill:#3fb95022,stroke:#3fb950,color:#adbac7;
    class RBTREE,RDLLIST,WAIT ep
    class ADD,MOD,DEL,EWAIT op

二、核心结构

// fs/eventpoll.c
struct eventpoll {
    struct rb_root_cached rbr;     // 红黑树:所有注册的 fd
    struct list_head rdllist;       // 就绪链表
    wait_queue_head_t wq;          // epoll_wait 等待队列
    // ...
};

struct epitem {
    struct rb_node rbn;             // 红黑树节点
    struct list_head rdllink;       // 就绪链表节点
    struct epoll_filefd ffd;        // fd + file*
    struct epoll_event event;       // 用户注册的事件
    // ...
};

三、epoll_ctl:注册 fd

epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
  1. 分配 epitem
  2. 插入红黑树(O(log n))
  3. 在目标 fd 的等待队列注册回调 ep_poll_callback

当 fd 有事件 → 回调把 epitem 加入就绪链表 → 唤醒 epoll_wait。

四、epoll_wait:收割事件

int n = epoll_wait(epfd, events, maxevents, timeout);
  1. 检查就绪链表 → 非空则直接返回
  2. 空 → 睡在 ep->wq → 等待回调唤醒
  3. 唤醒后遍历就绪链表 → copy_to_user → 返回

O(就绪 fd 数),不是 O(全部 fd 数)——这是 epoll 打败 select/poll 的关键。

五、LT vs ET

5.1 LT(Level-Triggered,默认)

fd 有数据可读 → 每次 epoll_wait 都返回。没读完 → 下次还返回。

5.2 ET(Edge-Triggered)

event.events = EPOLLIN | EPOLLET;

只在状态变化时通知一次。必须一次读到 EAGAIN。

ET 优势:减少 epoll_wait 返回次数。 ET 陷阱:不读完 → 可能长期不再通知。编程范式见 67 篇

5.3 内部实现

LT:就绪事件返回后,若 fd 仍有事件 → 重新加入就绪链表(ep_send_events 路径,见 57 篇 §4.5)。 ET:返回后 重新加入。

六、EPOLLEXCLUSIVE(4.5+)

event.events = EPOLLIN | EPOLLEXCLUSIVE;

多线程/多进程 epoll_wait 同一个 listen socket → 惊群(thundering herd)。

EPOLLEXCLUSIVE:只唤醒一个等待者。Nginx 1.11.3+ 使用。

七、EPOLLONESHOT

event.events = EPOLLIN | EPOLLONESHOT;

触发一次后自动禁用 → 需要 epoll_ctl(MOD) 重新启用。

保证每个 fd 同一时刻只被一个线程处理。

八、级联 epoll

epoll fd 本身也是 fd → 可以被另一个 epoll 监控 → 级联 epoll

用途:分层事件处理(不常用,有复杂性)。

九、epoll vs io_uring

机制对比、syscall 模型与适用场景见专文《epoll 与 io_uring 对比》。

特性 epoll io_uring
通知模型 就绪通知 完成通知
syscall epoll_wait + read/write 可批量化 / 零 syscall
适用 网络(成熟) 文件 + 网络
复杂度
生态 成熟 快速成长

大多数网络场景 → epoll 足够。文件 I/O + 极致性能 → 评估 io_uring。

十、开放问题

  1. 纯 TCP 服务何时值得默认 io_uring 而非 epoll——公开结论分裂,见对比文。
  2. EPOLL_BUSY_LOOP 与延迟/CPU 占用的 trade-off 仍缺统一工程指南。
  3. 级联 epoll 在 modern 框架中几乎绝迹,是否被 eventfd + 单 epoll 完全取代——无共识文档。

十一、小结


参考资料

规范 / 手册

源码

论文 / 报告

站内

工具


上一篇io_uring 内核内部 下一篇select/poll

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。

2026-05-23 · os

【操作系统百科】异步 I/O 模型 benchmark

epoll、io_uring、libaio、阻塞线程池——四种异步模型的真实性能对比。本文用统一 workload 量化 echo server、静态文件服务、数据库 I/O 场景下的吞吐、延迟与 CPU 开销。

2026-04-30 · os

【操作系统百科】NUMA 内存

多路服务器里内存不再平等——本地访问与跨 socket 远端访问的延迟与带宽不同。本文讲 NUMA 拓扑、mempolicy、AutoNUMA、NUMA balancing、CXL 分级内存,以及如何用 numactl/lscpu/numastat 核对拓扑并做生产调优;不编造本机未测得的纳秒延迟。

2026-04-18 · os

【操作系统百科】cgroup v2:资源控制的统一模型

cgroup v2 把 v1 的多 hierarchy 统一成单一树。本文讲 unified hierarchy、controller 清单、cpu.weight/io.weight/memory.max、PSI 压力指标、systemd slice/scope/service 层级、cgroup delegation 与 rootless、以及 cgroup v2 的诊断姿势。


By .