真实面经题目 · 原创解析
epoll 的 LT 和 ET 模式
这题考察 epoll 事件触发语义和网络编程细节,关键是说清 LT 反复提醒、ET 只提醒状态变化,以及 ET 必须配合非阻塞循环读写到 EAGAIN。
真实面经题目 · 原创解析
这题考察 epoll 事件触发语义和网络编程细节,关键是说清 LT 反复提醒、ET 只提醒状态变化,以及 ET 必须配合非阻塞循环读写到 EAGAIN。
epoll 的 LT 是水平触发,只要 fd 当前仍满足可读或可写条件,epoll_wait 就会继续返回这个事件,所以一次没处理完也会再次提醒。ET 是边缘触发,只在状态发生变化时通知,例如从不可读变为可读;通知后如果用户态没有把数据读完,剩余数据不一定再次触发事件。ET 通知次数少,适合高并发下减少重复唤醒,但编码要求更高:fd 要设为 non-blocking,收到读事件后循环 read 直到返回 EAGAIN/EWOULDBLOCK,写事件也要处理部分写和写缓冲,否则容易连接假死或线程阻塞。
LT 关心当前条件是否成立:缓冲区还有数据就继续通知。ET 关心条件是否发生变化:新数据到达或写缓冲从不可写变可写时通知一次。
LT 下应用一次只读一部分也没关系,只要内核缓冲区还可读,下次 epoll_wait 仍会返回。它更接近 select/poll 的使用体验,适合先保证正确性。
ET 不会因为同一批未读完数据反复提醒,能降低事件分发开销。但这种效率来自更严格的用户态处理:一次通知后要尽量把当前可处理内容清空。
ET 通常要 while 循环 read/write 到 EAGAIN。如果 fd 是阻塞的,最后一次没有数据可读时会卡住线程;如果不循环读完,剩余数据可能再也没有事件提醒。
可写通常很频繁,长期监听 EPOLLOUT 会导致空转。更常见做法是有待发送数据且一次没写完时再关注写事件,写完后取消关注。
同一个连接被多个线程处理时,可能需要 EPOLLONESHOT 或连接级状态锁,确保一个事件在处理完成前不会被多个 worker 同时操作。
如果收到事件后只读一次,内核缓冲区里剩余数据可能不会再次产生边缘事件,连接看起来还有数据却不再被处理。
因为正确做法是循环读写直到 EAGAIN。阻塞 fd 在最后一次没有数据时可能卡住线程,影响整个事件循环或 worker。
select/poll 每轮要传入并扫描 fd 集合;epoll 维护内核事件表和就绪队列,减少重复拷贝和全量扫描,适合大量连接但活跃连接较少的场景。
它让某个 fd 的事件触发一次后自动失效,常用于多线程模型,避免多个线程同时处理同一连接;处理完成后需要重新 arm。