epoll ET与LT模式详解

一、epoll ET与LT模式详解

1、ET与LT介绍

EPOLLET,就是边缘触发模式(Edge Trigger,ET),而默认的模式称为水平触发模式(Level Trigger,LT),区别在于:

  • 水平触发模式,一个事件只要有,就会一直触发
  • 边缘触发模式,只有一个事件从无到有才会触发

下面以fd的读写事件为例,介绍这两种工作模式,如下:

场景一:fd读事件触发条件

水平模式:
1)fd上无数据 => fd上有数据
2)fd处于有数据状态
边缘模式:
1)fd上无数据 => fd上有数据
2)fd又新来一次数据

场景二:fd写事件触发条件

水平模式:
1)fd一直处于可写状态
2)fd不可写 => fd可写
边缘触发:
1)fd不可写 => fd可写
2)fd可以写的空间发生变化

2、通过Demo讲解ET与LT的差异

程序一:监听读事件,并分别设置成ET与LT模式

#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>

int main(void)
{
    int epfd, nfds;
    struct epoll_event ev, events[5]; // ev用于注册事件,数组用于返回要处理的事件
    epfd = epoll_create(1);           // 只需要监听一个描述符——标准输入
    ev.data.fd = STDOUT_FILENO;
    ev.events = EPOLLIN | EPOLLET;                      // 监听读状态同时设置ET模式
    epoll_ctl(epfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev); // 注册epoll事件
    for (;;) {
        nfds = epoll_wait(epfd, events, 5, -1);
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == STDOUT_FILENO) {
                printf("welcome to epoll's word!\n");
            }
        }
    }

    return 0;
}

输出结果

[root@kwepcsf00018 epoll_demo]# ./demo
hello world
welcome to epoll's word!
hello world
welcome to epoll's word!

1)当输入一组字符串,这组字符被送入buffer,字符停留在buffer中,又因为buffer由空变为不空,所以ET返回读就绪,输出welcome to epoll’s world!。
2)之后程序再次执行epoll_wait,此时虽然buffer中有内容可读,但是根据上节的分析,ET并不返回就绪,导致epoll_wait阻塞。
3)再次输入一组字符,导致buffer中的内容增多,根据上节的分析这将导致fd状态的改变,是对应的epitem再次加入rdlist,从而使epoll_wait返回读就绪,再次输出welcome to epoll’s world!。

修改模式为LT模式
ev.events=EPOLLIN;    // 默认使用LT模式

输出结果

welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!

程序陷入死循环,因为输入任意字符串后,数据被送入buffer且没有被读出,所以LT模式下每次epoll_wait都认为buffer可读返回读就绪,导致每次都会输出”welcome to epoll’s world!”

程序二:监听读事件,当有数据可读时,直接把buffer中的数据read出来

#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>

int main(void)
{
    int epfd, nfds;
    struct epoll_event ev, events[5]; // ev用于注册事件,数组用于返回要处理的事件
    epfd = epoll_create(1);           // 只需要监听一个描述符——标准输入
    ev.data.fd = STDOUT_FILENO;
    ev.events = EPOLLIN;                                // 监听读状态同时设置ET模式
    epoll_ctl(epfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev); // 注册epoll事件
    for (;;) {
        nfds = epoll_wait(epfd, events, 5, -1);
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == STDOUT_FILENO) {
				char buf[1024] = {0};
                read(STDIN_FILENO, buf, sizeof(buf));
                printf("welcome to epoll's word!\n");
            }
        }
    }

    return 0;
}

输出结果

[root@kwepcsf00018 epoll_demo]# ./demo
hello world
welcome to epoll's word!
hello world
welcome to epoll's word!

本程序依然使用LT模式,但是每次epoll_wait返回读就绪的时候都将buffer(缓冲)中的内容read出来,所以导致buffer再次清空,下次调用epoll_wait就会阻塞。所以能够实现所想要的功能——当用户从控制台有任何输入操作时,输出”welcome to epoll’s world!”

程序三:监听写事件,并设置ET模式

#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>

int main(void)
{
    int epfd, nfds;
    struct epoll_event ev, events[5]; // ev用于注册事件,数组用于返回要处理的事件
    epfd = epoll_create(1);           // 只需要监听一个描述符——标准输入
    ev.data.fd = STDOUT_FILENO;
    ev.events = EPOLLOUT | EPOLLET;                     // 监听写状态同时设置ET模式
    epoll_ctl(epfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev); // 注册epoll事件
    for (;;) {
        nfds = epoll_wait(epfd, events, 5, -1);
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == STDOUT_FILENO) {
                printf("welcome to epoll's word!\n");
            }
        }
    }

    return 0;
}

输出结果

welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!
welcome to epoll's word!

1)首先初始buffer为空,buffer中有空间可写,这时无论是ET还是LT都会将对应的epitem加入rdlist,导致epoll_wait就返回写就绪。
2)程序向标准输出输出”welcome to epoll’s world”和换行符,因为标准输出为控制台的时候缓冲是“行缓冲”,所以换行符导致buffer中的内容清空,当有旧数据被发送走时,即buffer中待写的内容变少的时候会触发fd状态的改变,所以下次epoll_wait会返回写就绪,如此循环往复

程序四:使用printf输出时,去掉\n

与程序三相比,程序四只是将输出语句的printf的换行符移除。看到程序成挂起状态。因为第一次epoll_wait返回写就绪后,程序向标准输出的buffer中写入“welcome to epoll’s world!”,但是因为没有输出换行,所以buffer中的内容一直存在,下次epoll_wait的时候,虽然有写空间但是ET模式下不再返回写就绪。

解决上面两个EL模式下的读写问题,实现方案:

1)对于读,只要buffer中还有数据就一直读
2)对于写,只要buffer还有空间且用户请求写的数据还未写完,就一直写

本章小结

LT:水平触发,效率会低于ET触发,尤其在大并发,大流量的情况下。但是LT对代码编写要求比较低,不容易出现问题。LT模式服务编写上的表现是:只要有数据没有被获取,内核就不断通知你,因此不用担心事件丢失的情况
ET:边缘触发,效率非常高,在并发,大流量的情况下,会比LT少很多epoll的系统调用,因此效率高。但是对编程要求高,需要细致的处理每个请求,否则容易发生丢失事件的情况。从本质上讲:与LT相比,ET模型是通过减少系统调用来达到提高并行效率

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值