✍个人博客:https://blog.csdn.net/Newin2020?type=blog
📣专栏地址:http://t.csdnimg.cn/fYaBd
📚专栏简介:在这个专栏中,我将会分享 C++ 面试中常见的面试题给大家~
❤️如果有收获的话,欢迎点赞👍收藏📁,您的支持就是我创作的最大动力💪
172. epoll 的工作模式
- LT 模式 (水平触发)
假设委托内核检测读事件 -> 检测 fd 的读缓冲区
读缓冲区有数据 - > epoll 检测到了会给用户通知
- 用户不读数据,数据一直在缓冲区,epoll 会一直通知
- 用户只读了一部分数据,epoll 会通知
- 缓冲区的数据读完了,不通知
LT(level - triggered)是缺省的工作方式,并且同时支持 block 和 no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的 fd 进行 IO 操作。如果你不作任何操作,内核还是会继续通知你的。
- ET (边沿触发)
假设委托内核检测读事件 -> 检测 fd 的读缓冲区
读缓冲区有数据 - > epoll 检测到了会给用户通知
- 用户不读数据,数据一直在缓冲区中,epoll 下次检测的时候就不通知了
- 用户只读了一部分数据,epoll 不通知
- 缓冲区的数据读完了,不通知
ET(edge - triggered)是高速工作方式,只支持 no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过 epoll 告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了。但是请注意,如果一直不对这个 fd 作 IO 操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once)。
ET 模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll 工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。
173. LT 模式效率更高,还是 ET 模式效率更高?
我注意到 flamingo 服务器 accept,read,write 都是一个事件调用一次。我看某个库 epoll LT 模式是在 while 循环内进行 accpet,read,write 直到 EAGAIN 的,我个人认为循环调用可以减少事件触发的次数从而提高效率。但是有人说在循环内处理调用会导致该线程一直被这个 fd 占用,比如写 100M 的文件,这时候别的 fd 都不能及时处理了。群主觉得 LT 模式在循环内处理读写调用怎么样?
一次事件处理一次,这个处理机会通过回调提供出去,更甚可以把 fd 暴露出去让用户自己去怎么收。epoll LT 模式灵活就在这里,既可以一次性收一点,也可以一次性收完(收到 eagain)。flamingo 是 LT 模式。ET 模式是必须收完的,你说的对,如果某些 fd 经常有数据,且数据量大,ET 模式就会一直占着线程 cpu。那么其他 fd 就可能饿死了。举个例子,做内网视频监控的,每个连接上视频流都很大,你不能把一个连接上处理完再处理其他的。那样其他用户体验就不好了。所以,flamingo 没采用你说的方式。而是把接口暴露出来,按需处理。退一步说,某一连接突然来了很大一段数据,可以拆分成几十个数据包。那为什么不先处理一些呢,如果放在循环里面就必须先把数据收完再处理了。业务线程这样闲的时候闲死,忙的时候忙死。
所以到底选择 ET 模式还是 LT 模式要结合业务。有的应用是数据包频率高,单个包数据不大,有的业务是单个包大,但数据频率不高,有的是兼顾二者。所以要综合考虑,结合业务场景。这也是为什么大多数网络库都不限定必须用 LT 或 ET 模式。
174. IO 特别密集时 epoll 效率还高吗?
可以先解释 io 特别密集时为什么 epoll 效率不高。原因是:
- 连接密集(短连接特别多),使用 epoll 的话,每一次连接需要发生 epoll_wait -> accpet -> epoll_ctl 调用,而使用 select 只需要 select->accpet,减少了一次系统调用。
- 读写密集的话,如果收到数据,我们需要响应数据的话,使用 epoll 的情况下, read 完后也需要 epoll_ctl 加入写事件,相比 select 多了一次系统调用