60 秒回答模板

epoll 的 LT 是水平触发,只要 fd 当前仍满足可读或可写条件,epoll_wait 就会继续返回这个事件,所以一次没处理完也会再次提醒。ET 是边缘触发,只在状态发生变化时通知,例如从不可读变为可读;通知后如果用户态没有把数据读完,剩余数据不一定再次触发事件。ET 通知次数少,适合高并发下减少重复唤醒,但编码要求更高:fd 要设为 non-blocking,收到读事件后循环 read 直到返回 EAGAIN/EWOULDBLOCK,写事件也要处理部分写和写缓冲,否则容易连接假死或线程阻塞。

考点 I/O 多路复用
难度 Linux 网络编程经典题
回答目标 讲清触发条件和编码风险

深入解析

01

先定义触发条件

LT 关心当前条件是否成立:缓冲区还有数据就继续通知。ET 关心条件是否发生变化:新数据到达或写缓冲从不可写变可写时通知一次。

02

LT 的优势是容错简单

LT 下应用一次只读一部分也没关系,只要内核缓冲区还可读,下次 epoll_wait 仍会返回。它更接近 select/poll 的使用体验,适合先保证正确性。

03

ET 的优势是减少重复唤醒

ET 不会因为同一批未读完数据反复提醒,能降低事件分发开销。但这种效率来自更严格的用户态处理:一次通知后要尽量把当前可处理内容清空。

04

非阻塞是 ET 的必要搭配

ET 通常要 while 循环 read/write 到 EAGAIN。如果 fd 是阻塞的,最后一次没有数据可读时会卡住线程;如果不循环读完,剩余数据可能再也没有事件提醒。

05

写事件也要谨慎注册

可写通常很频繁,长期监听 EPOLLOUT 会导致空转。更常见做法是有待发送数据且一次没写完时再关注写事件,写完后取消关注。

06

多线程处理要防重复消费

同一个连接被多个线程处理时,可能需要 EPOLLONESHOT 或连接级状态锁,确保一个事件在处理完成前不会被多个 worker 同时操作。

易错点

  • 把 ET 简单说成性能一定更好,不提非阻塞、循环读写和遗漏事件风险。
  • ET 读事件只 read 一次,没有读到 EAGAIN 或 EWOULDBLOCK。
  • 长期监听可写事件,导致 fd 一直可写时事件循环空转。
  • 把 epoll 的高性能理解成没有任何扫描和系统调用成本,忽略活跃连接比例和业务处理耗时。

面试官追问

ET 模式为什么容易连接假死?

如果收到事件后只读一次,内核缓冲区里剩余数据可能不会再次产生边缘事件,连接看起来还有数据却不再被处理。

为什么 ET 下 fd 要设置为非阻塞?

因为正确做法是循环读写直到 EAGAIN。阻塞 fd 在最后一次没有数据时可能卡住线程,影响整个事件循环或 worker。

select、poll、epoll 的核心区别是什么?

select/poll 每轮要传入并扫描 fd 集合;epoll 维护内核事件表和就绪队列,减少重复拷贝和全量扫描,适合大量连接但活跃连接较少的场景。

EPOLLONESHOT 解决什么问题?

它让某个 fd 的事件触发一次后自动失效,常用于多线程模型,避免多个线程同时处理同一连接;处理完成后需要重新 arm。