结论:LT(默认)是状态通知——只要 fd 处于就绪状态(读缓冲区有数据/写缓冲区有空位),每次 epoll_wait 都报告;ET 是事件通知——只在状态从"不就绪"变"就绪"的边沿报告一次。ET 通知少、性能上限高,但要求应用每次必须把数据读到 EAGAIN 为止,否则剩余数据再也不会触发通知。
展开:LT 编程简单:可以每次只读一部分,下次 epoll 还会提醒你,配合阻塞/非阻塞 fd 都行。ET 的约束链条:必须非阻塞 fd + 循环 read/write 直到 EAGAIN——阻塞 fd 在 ET 下最后一次 read 可能挂住整个事件循环。ET 的价值在于减少 epoll 唤醒次数和避免"惊群"类问题:高并发下同一个持续就绪的 fd 在 LT 下每次 wait 都返回它,ET 只在数据到达瞬间通知一次。nginx 用 ET,Redis 用 LT(代码简单,性能损失在可接受范围)。易错点:1)ET 下 accept 也要循环到 EAGAIN,否则积压的连接丢失;2)EPOLLONESHOT 常与 ET 组合解决多线程同时处理同一 fd 的事件竞争。
// ET 模式的标准读法
for (;;) {
ssize_t n = read(fd, buf, sizeof buf);
if (n < 0 && errno == EAGAIN) break; // 读尽了
if (n <= 0) { /* 关闭或出错 */ break; }
handle(buf, n);
}
追问方向:epoll 惊群问题与 SO_REUSEPORT、io_uring 相比 epoll 的代际差异。