认识epoll模型的两种工作方式 以及 ET模式必须设为非阻塞的原因

epoll有两种工作方式,一种是LT模式(level trigger,水平触发),一种是ET模式(edge trigger,边缘触发)。默认情况下是LT模式。


目录

1、认识epoll模型的工作方式

(1) LT模式

(2) ET模式

2、ET模式必须将文件描述符设为非阻塞的原因

(1) 情景介绍

(2) 原因分析

(3) 解决方案(设为非阻塞的方法)

3、使用ET模式的两个要求

4、总结


1、认识epoll模型的工作方式

(1) LT模式

以读事件为例,当缓冲区有数据准备好的时候,此时会触发读事件,如果我们一直不去读取缓冲区里的数据,epoll模型就会一直通知我们有事件就绪,即epoll_wait中的events参数就会一直包含某个文件描述符的读事件。

这就是LT模式,也是epoll模型的默认模式。

(2) ET模式

ET模式与LT模式相反,当缓冲区就数据准备好的时候,也会触发读事件,但是只会触发一次,如果我们这次没有调用read/recv读取 或者 没有一次读完,后面就不会通知有读事件就绪了。简单来说,只有当缓冲区里的数据量发生变化的时候,才会通知我们一次,不会像LT模式那样一直通知。

这就是ET模式,单纯的从通知效率这个角度来看,ET模式的效率更高,因为不会重复通知某一个事件就绪。

2、ET模式必须将文件描述符设为非阻塞的原因

既然这是一个必要条件,那必然存在对应的情景来要求我们将文件描述符设为非阻塞模式。

(1) 情景介绍

客户端给服务端发送了一个大小为100K byte的数据,读事件就绪,epoll模型通知你一次,但是服务端一次没有读完,还剩余50K在缓冲区里。等到下一次调用epoll_wait的时候,由于epoll是ET模式,已经通知过你一次,这次就不认为sock有读事件就绪,epoll_wait 也就不会返回该文件描述符上读事件就绪的信息。

因此,我们需要将数据一次读完,那么这跟设为非阻塞模式有什么关系?下面细说。

(2) 原因分析

上面提到,我们需要将数据一次读完,因此我们需要循环读取数据,就像下面这样。

假设每次读取1024个字节,等读完这100K个字节时,recv发现缓冲区里没有数据了,此时会默认进入阻塞状态,等待数据就绪,这就严重影响到后面的文件描述符读取/写入内容了。所以ET模式下必须要设为非阻塞。

(3) 解决方案(设为非阻塞的方法)

如果使用recv函数读取数据

一般我们可以设置recv的最后一个参数来设为非阻塞模式,但是因为非阻塞模式下,没有数据可读的时候,返回0,这就跟对端关闭连接一样,不容易区分。

因此这里我们采用的是通过recv返回的字节大小判断是否全部读完。现在数据量的大小是100K字节,每次读取1024个字节,最后一次读取的时候,只有672个字节,说明这是最后一批数据了,读完这批数据就可以直接跳出循环了。

#define SIZE 1024
char recvBuffer[SIZE] = {0};
for(;;)
{
    ssize_t s = recv(sock, recvBuffer,sizeof(recvBuffer)-1,0);
    if(s > 0){
        // 其他处理操作...

        if(s <= SIZE){
            //说明这是最后一批数据了
            break;
        }
    }
}

如果使用的是read读取数据

如果使用的是read读取数据,我们可以在读取数据之前使用fcntl 接口来将文件描述符设为非阻塞模式,下面是已经封装好的函数。

bool SetNoBlock() {
 int fl = fcntl(fd_, F_GETFL);    //获取之前的状态信息
 if (fl < 0) {
     perror("fcntl F_GETFL");
     return false;
 }
 int ret = fcntl(fd_, F_SETFL, fl | O_NONBLOCK);    //在之前状态信息的基础上添加非阻塞状态
 if (ret < 0) {
     perror("fcntl F_SETFL");
     return false;
 }
 return true;
}

3、使用ET模式的两个要求

要求一:必须要一次读完/写入所有的数据。因为ET模式只会通知一次,下一次读取只能是缓冲区接收到了新的数据。

要求二:必须设为非阻塞模式。循环读取的时候,如果缓冲区没有数据或者低于水位线,recv/read就会阻塞等待读事件就绪,这会影响到epoll模型中其他文件描述符的操作。

4、总结

LT 模式(水平触发,默认)只要有数据都会触发,缓冲区剩余未读尽的数据会导致epoll_wait返回

ET模式(边缘触发)只有数据到来才触发,不管缓存区中是否还有数据,缓冲区剩余未读尽的数据不会导致epoll_wait返回;

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值