浅谈 epoll 中 ET 和 LT 与 阻塞/非阻塞 IO
epoll 中有两种触发模式,LT (水平触发) 和 ET(边缘触发),网上关于这两种的介绍很多,在这里不多赘述,只简单说下这两种模式下使用 阻塞/非阻塞 IO 的情况,以及对于 “为什么 ET 模式必须使用非阻塞 IO ?” 这个问题的看法。
个人认为使用 阻塞IO 潜在的问题在于,使用 阻塞 IO 去读的时候,会导致在没有数据可读的时候,导致当前工作线程阻塞不工作。而 ET 模式与 LT 模式都是在有数据的情况下触发,只不过触发的时机不同。假定读缓冲区 50b,而收到的包为 100b,有如下情况:
- 阻塞 IO
LT 模式下,由于只要有数据就会触发读,因此不会有问题,但是在 ET 模式下,由于在新的数据到来之前,都不会触发读事件,因此会导致剩下的 50b 没有读取到,所以为了保证能够读取到完整的包,需要使用 while(1) 之类的循环去读,这就会导致在数据读完之后,最后一次 read 阻塞,因为所有的数据都已经读完了。
- 非阻塞 IO
在 LT 模式下,使用非阻塞 IO 的效果与阻塞 IO 差不多,在 ET 模式下,处理的逻辑与上面类似,但是由于使用的 非阻塞 IO ,因此不会导致最后一次 read 阻塞,而是会返回 EAGAIN 。
最后对于 “为什么 ET 模式必须使用非阻塞 IO ?” 这个问题。我的看法是应该将 “必须” 改成 “建议”,因为如果使用 阻塞IO ,也是有办法规避上面的问题的,比如先获取包体的大小之类的,但是这样也会提高复杂度,效率也会更低下。对于监听的 socket,最好使用 LT 模式,ET 模式会导致高并发情况下,有的客户端会连接不上,除非使用 while 循环 accpet,且为非阻塞 socket 。对于读写的 socket,LT 模式下,阻塞和非阻塞效果都一样。ET 模式下,建议使用非阻塞 IO,并一次性地完整读写全部数据。