Back to VNotes

Linux / Network

epoll ET 模式要点

ET 模式的核心是“只在状态变化时提醒一次”,所以读写循环必须把缓冲区尽量处理干净。

LT 和 ET 的差异

LT 模式更像反复提醒:只要 fd 上还有数据,下次 epoll_wait 仍然可能返回。ET 模式只在状态从不可读变成可读时提醒一次,如果这次没有读完,后续可能不会再提醒。

读事件处理

ET 模式下 read/recv 通常要放进循环,直到返回 EAGAIN 或 EWOULDBLOCK。这个错误不是失败,而是在非阻塞 fd 上表示“当前已经没有更多数据可读”。

  • fd 必须设置为非阻塞。
  • 循环读到 EAGAIN 才能退出。
  • 返回 0 表示对端关闭连接。
  • 真实错误需要关闭 fd 或进入错误处理路径。

写事件处理

写事件同理,不能假设一次 send 就能发完响应。需要记录已经发送的偏移,剩余内容等下一次可写事件继续发。

while (sent < response.size()) {
    ssize_t n = send(fd, response.data() + sent, response.size() - sent, 0);
    if (n > 0) sent += n;
    else if (errno == EAGAIN || errno == EWOULDBLOCK) break;
    else close_connection(fd);
}

调试清单

  • 连接卡住时先看 fd 是否非阻塞。
  • CPU 占用异常时检查是否反复注册无意义写事件。
  • 响应不完整时检查写偏移是否保存。
  • 偶现丢请求时检查读循环是否提前退出。

事件循环中的易错点

ET 模式最怕“以为自己读完了”。只要一次事件没有把内核缓冲区读到 EAGAIN,后面就可能再也收不到提醒。写事件也一样,如果 send 只写出一部分,必须保存偏移量。

  • 读事件循环到 EAGAIN 再退出。
  • 写事件只在有待发送数据时注册。
  • 连接关闭、读 0、真实错误要分开处理。

复习问题

复习时可以问自己三个问题:fd 是否非阻塞?读写循环是否处理 EAGAIN?半包和粘包由哪个缓冲区负责?如果这三个问题答得清楚,ET 模式基本就不会乱。