LT自动挡,ET手动挡(epoll)

epoll有 ET和LT两种模式, 默认是LT模式。

LT模式的时候,epoll_wait 会把有事件的 file 再次加到 rdllist 列表中,以便下次epoll_wait可以再检查一遍。

            if (epi->event.events & EPOLLONESHOT)
                epi->event.events &= EP_PRIVATE_BITS;
            else if (!(epi->event.events & EPOLLET)) { //LT模式
                /*
                 * If this file has been added with Level
                 * Trigger mode, we need to insert back inside
                 * the ready list, so that the next call to
                 * epoll_wait() will check again the events
                 * availability. At this point, noone can insert
                 * into ep->rdllist besides us. The epoll_ctl()
                 * callers are locked out by
                 * ep_scan_ready_list() holding "mtx" and the
                 * poll callback will queue them in ep->ovflist.
                 */
                list_add_tail(&epi->rdllink, &ep->rdllist);
            }

LT 模式下,状态不会丢失,程序完全可以于 epoll 来驱动。

ET模式下,程序首先要自己驱动逻辑,如果遇到 EAGAIN错误的时候,就要依赖epoll_wait来驱动,这时epoll帮助程序从阻碍中脱离。


所以LT是自动挡, ET是手动挡。

 

ET 边沿触发的 边沿是 AGAIN错误,属于下降沿触发。

 

ET 的驱动事件依靠 socket的 sk_sleep 等待队列唤醒,这只有在有新包到来才能发生,数据包导致POLLIN, ACK确认导致 sk_buffer destroy从而导致POLLOUT, 但这不是一对一得关系,是多对一(多个网络包产生一个POLLIN, POLLOUT事件)。

 

 ET常见错误:

 recv到了合适的长度, 程序处理完毕后就epoll_wait

 这时程序可能长期阻塞,因为这时socket的 rev_buffer里还有数据,或对端close了连接,但这些信息都在上次通知了你,你没有处理完,就epoll_wait了

 正确做法是:

recv到了合适的长度, 程序处理; 再次recv, 若果是EAGAIN则epoll_wait。


使用ET模式时, 程序自己驱动下,会发生socket被关闭的情况,这时要处理EPIPE信号和返回值。(如果不处理EPIPE那么会导致程序core掉)

总结:ET 使用准则,只有出现EAGAIN错误才调用epoll_wait。

 

场景:短连接,短数据包(一个recv和send就可以完成所有的网络操作),这种情形下测试的结果是ET和LT一样的效率。(所以不要以为用了ET效率就有提高)

strace -c -fF ./your_prog 后得到信息是 epoll_ctl 消耗的时间比 recv+send 还多。 

总结原因:整个程序是以网络事件来驱动的,所以每个连接都要epoll_ctl 3次; 如果程序自己主动recv+send, 不行的时候再网络驱动的话,可以节省这些epoll_ctl开销。

 

对于长连接,大数据包应用,因为 LT模式只能设置当时感兴趣的事件(如果不写数据也设置POLLOUT的话,会导致cpu 100%) ,所以要频繁调用epoll_ctl,内核也要多次操作链表,所以效率会比ET模式低。

 

 

posted @ 2010-07-20 17:59  napoleon_liu  阅读(4466)  评论(1编辑  收藏  举报