仔细体会 epoll中的et lt模式

网上很多关于EPOLL的介绍,或者翻译man手册,或者找一些国外的文章,还有一些很好得心得分享。但是看完总有一种模糊的感觉,尤其是关于 ET LT 两种模式,今天自己做一些例子仔细体会下。

一:概念

  边沿触发(edge triggered),就是在事务的两个状态交替的边沿触发,对socket来讲就是的新的数据来的时候触发,如果是上一次的数据,你没有收完 则不会再次提醒。除非有新的数据到来。

  电平触发(levle triggered),所谓电平触发只要是事务的某一状态出现就触发,对sockets来讲就是只有内核缓冲区中有数据,就会触发。而不管这数据是你上次没收完,还是新来数据。

  (以上摘自互联网)

  理解:上面提到的状态及交替,以socket接收到新的数据为分割,如果接到了新的数据,则认为交替了,没有收到新数据则认为是上一个状态未结束。

  

  代码场景:

  •    1:ET模式,得到可读事件,但是不去read数据,看epoll_wait以后是否有事件提醒。

    2:LT模式,得到可读事件,但是不去read数据,看epoll_wait以后是否有事件提醒。

  •  1:ET模式,没有及时读取数据,再次有新数据到来,能不能继续读取上次的数据。

  

  验证结果:ET模式中,接收到新数据,给数据提醒,无论读不读数据,读取多少,都只提醒一次。如果没有及时读取,则下次有新数据到来,则读取上次数据。

         LT模式中,接收到新数据,给数据提醒,只要不读完数据,一直提醒。

二:如何处理ET模式场景

  解决此问题有两种方法:
  1.采用非阻塞函数,将数据收完后,再次调用epoll_wait(),对应epoll man手册的描述为

    i with non-blocking file descriptors

    ii by going to wait for an event only after read(2) or write(2) return EAGAIN or EWOULDBLOCK

  2.在接收数据后,不管有没有收完,都调用 epoll_ctl() with EPOLL_CTL_MOD,这样相当于重新设置事件,就可以收到数据。

  (以上摘自互联网)

  理解:如果只提醒一次,那我们就应该在用户态的代码中,"尽量完整" 并且 "尽量减少IO操作中CPU的等待" 得完成内核缓冲区的数据的读取。所以方法1中,第一步置为非阻塞,第二步中的"EAGAIN or EWOULDBLOCK"要做处理,但是没必要非等到返回这两个东西。

  附代码片段:

conn->recv_ready=1;
    do{
        for(;;){
            if(conn->recv_ready==0){
                break;
            }
            printf("recv starting ......
");
            ret=read(conn->fd,buffer,len);
            if(ret>0){
                if(ret<(ssize_t)len){
                    conn->recv_ready=0;
                }
                buffer+=ret;
                conn->recv_bytes+=(ssize_t)ret;
            }

            if(ret==0){
                printf("recv done......
");
                return;
            }

            if(ret<0){
                if(errno==EINTR){
                    printf("recv on sd %d not ready eintr
",conn->fd);
                    continue;
                }else if(errno==EAGAIN||errno==EWOULDBLOCK){
                    printf("recv on sd %d not ready eagain
",conn->fd);
                    conn->recv_ready=0;
                    return;
                }else{
                    printf("recv on sd %d error
",conn->fd);
                    conn->recv_ready = 0;
                    return;
                }
            }
        }
    }while(conn->recv_ready);
原文地址:https://www.cnblogs.com/claresun/p/3687104.html