epoll的LT和ET(转)

1 socket IO事件

1.1 读事件

读事件:句柄从不可读变成可读,或者句柄写缓冲区有新的数据进来且超过SO_RCVLOWAT。
常见的产生读事件有如下几种:

  • socket有一个未清除的错误。如非阻塞的connect连接错误会使socket变成可读写状态。
  • 非阻塞accept有新的连接进来。
  • socket写对端关闭,read返回0。
  • socket读缓冲区有新的数据进来且超过SO_RCVLOWAT

1.2 写事件

写事件:句柄从不可写变成可写,或者句柄写缓冲区有新的数据进来而且缓冲区水位高于SO_SNDLOWAT。常见的写事件事件有如下几种:

  • socket有一个未清除的错误。例如非阻塞connect连接出错会导致socket变成可读可写状态。
  • 非阻塞connect连接成功后端口状态会变成可写。
  • socket读对端关闭,socket变成可写状态,产生SIGPIPE信号。
  • socket写缓冲区有新的数据进来且超过SO_SNDLOWAT
    在epoll中,读事件对应EPOLLIN,写事件对应EPOLLOUT。

2 ET

句柄在发生读写事件时只会通知用户一次
ET模式主要关注fd从不可用到可用或者可用到不可用的情况。
ET只支持非阻塞模式。

2.1 应用层逻辑

ET模式下读写操作要时用wihle循环,直到读/写够足够多的数据,或者读/写到返回EAGAIN。尤其时在写大块数据时,一次write操作不足以写完全部数据,或者在读大块数据时,应用层缓冲区数据太小,一次read操作不足以读完全部数据,应用层要么一直调用while循环一直IO到EGAIN,或者自己调用epoll_ctl手动触发ET响应。

2.2 优缺点

缺点:应用层业务逻辑复杂,容易遗漏事件,很难用好。
优点:相对LT模式效率比较高。

3 LT

只要句柄一直处于可用状态,就会一直通知用户。
LT模式下,句柄读缓冲区被读空后,句柄会从可用转变未不可以用,这个时候不会通知用户。写缓冲区只要还没写满,就会一直通知用户。
LT模式支持阻塞和非阻塞两种方式。epoll默认的模式是LT。
LT下,应用层的业务逻辑比较简单,更不容易遗漏事件,更不容易出错。通常,在将数据写完后,我们会关闭句柄的写事件。

3.1 优缺点

优点:编程更符合用户直觉,业务层逻辑更简单。
缺点:效率比ET低。

3.2 ET比LT更高效得原因

ET在通知用户后,就会把fd从就绪队列里删除。而LT通知用户后fd还在就绪链表中,随着fd的增多,就绪链表越大。下次epoll要通知用户时还需要遍历整个就绪链表。遍历的性能是线性,如果fd的数量非常多,就会带来比较显著的效率下降。
同样数量的fd下,LT模式维护的就绪链表比ET的大。

3.3 非阻塞模式下的accept该用什么触发模式?

LT和ET各有优缺点。使用哪种模式取决于并发量。当并发量比较小时,比较推荐LT,因为LT模式下应用的读写逻辑比较简单,不容易遗漏事件,代码不易出错好维护,而且性能损失不大。当并发量非常大时,推荐使用ET模式,可以有效提升EPOLL效率。

3.4 LT模式下,可写状态的fd会一直触发事件,该怎么处理这个问题

  • 方法1:每次要写数据时,将fd绑定EPOLLOUT事件,写完后将fd同EPOLLOUT从epoll中移除。
  • 方法2:方法一中每次写数据都要操作epoll。如果数据量很少,socket很容易将数据发送出去。可以考虑改成:数据量很少时直接send,数据量很多时在采用方法1.

应用

使用ET的例子

nginx

使用LT的例子

redis

上一篇:AppBoxFuture(四). 随需而变-Online Schema Change


下一篇:[经验] - JQuery.Ajax + 跨域 (crossDomain) + POST + JSON + WCF RESTful, 5大陷阱和解决方案