事件触发模式 LT ET ?EPOLLIN EPOLLOUT 各种情况总结。【面试复盘】【学习笔记】

麻了,对 epoll 的触发机制理解不深刻…面试又被拷打了…

下面总结一下各种情况,并不涉及底层原理,底层原理看这里。

文章结构可以看左下角目录、

有什么理解的不对的,请大佬们指点。

先说结论,下面再验证:

  • LT(水平触发)

    • EPOLLIN 触发条件
         读缓冲区有数据就一直触发(即epoll_wait时能检测到),没有就不触发。

    • EPOLLOUT 触发条件:
          写缓冲区有空间可写,则一直触发。

  • ET(边缘触发)

    • EPOLLIN 触发条件:
          1. 当读 buff 从 空 -> 不空 时,触发;
          2. 当有新数据到达时,即读 buff 数据由 少 -> 多 时,触发;
          3. 当读 buff 有数据可读时,我们不处理,但是对相应fd进行epoll_mod IN事件时,触发。

    • EPOLLOUT 触发条件:
          1. 当写 buff 从 满 -> 不满 时,触发;
          2. 当有数据被送走时,即写 buff 数据由 多 -> 少 时,触发;
          3. 当写 buff 有数据,但是我们没处理(没发送出去),但是对相应fd进行epoll_mod OUT事件时,触发。

可以简单总结就是:
LT模式: 读buff有数据 / 写buff有空间,就触发;
ET模式: 读buff有数据,且数据减少 或 MOD时 / 写 buff 空间增加或MOD时,才触发。

验证:

下面将从几方面验证上面的结论:

LT模式
   检测EPOLLIN
      不读出数据
      读出数据
   检测EPOLLOUT
      不刷新缓冲区
      刷新缓冲区

ET模式
   检测EPOLLIN(1)
      不读出数据
      读出数据
   检测EPOLLIN(2,用MOD)
      不读出数据
      读出数据
   检测EPOLLOUT(1)
      不刷新缓冲区
      刷新缓冲区
   检测EPOLLOUT(2,用MOD)
      不刷新缓冲区
      刷新缓冲区

LT模式:

第一种:LT模式,检测EPOLLIN,未将数据读出

#include <unistd.h>
#include <iostream>
#include <sys/epoll.h>
using namespace std;

int main()
{
    int epfd = epoll_create(1); //创建epoll实例

    struct epoll_event ev,event[5];

    //设置参数
    ev.data.fd = STDIN_FILENO;  //接受键盘输入
    ev.events = EPOLLIN;    //默认就是LT模式
    epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev); //监听键盘输入文件描述符

    while(1)
    {
        int nfd = epoll_wait(epfd,event,5,-1);

        for(int i=0;i<nfd;i++)
            if(event[i].data.fd == STDIN_FILENO)
            {
                cout<<"LT 模式下 EPOLLIN,未将数据读出\n";
                sleep(1);
            }
    }

    return 0;
}

结果:
输入test回车后,死循环;
证明socket读缓冲区有数据,则会一直触发EPOLLIN
在这里插入图片描述



第二种:LT模式,测试EPOLLIN,将数据读出

#include <unistd.h>
#include <iostream>
#include <sys/epoll.h>
using namespace std;

int main()
{
    int epfd = epoll_create(1); //创建epoll实例

    struct epoll_event ev,event[5];

    //设置参数
    ev.data.fd = STDIN_FILENO;  //接受键盘输入
    ev.events = EPOLLIN;    //默认就是LT模式
    epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev); //监听键盘输入文件描述符

    while(1)
    {
        int nfd = epoll_wait(epfd,event,5,-1);

        for(int i=0;i<nfd;i++)
            if(event[i].data.fd == STDIN_FILENO)
            {
                char buff[128];
                read(STDIN_FILENO,buff,sizeof(buff));
                cout<<"LT 模式下 EPOLLIN,读出数据\n";
                sleep(1);
            }
    }

    return 0;
}

结果:
当我们将数据读出来后,EPOLLIN不触发了;
即socket读缓冲区没数据,不触发EPOLLIN。

在这里插入图片描述



第三种:LT模式,测试EPOLLOUT,不刷新缓冲区

#include <unistd.h>
#include <iostream>
#include <sys/epoll.h>
using namespace std;

int main()
{
    int epfd = epoll_create(1); //创建epoll实例

    struct epoll_event ev,event[5];

    //设置参数
    ev.data.fd = STDOUT_FILENO;  //检测输出缓冲区
    ev.events = EPOLLOUT;    //默认就是LT模式
    epoll_ctl(epfd,EPOLL_CTL_ADD,STDOUT_FILENO,&ev); //监听输出文件描述符

    while(1)
    {
        int nfd = epoll_wait(epfd,event,5,-1);

        for(int i=0;i<nfd;i++)
            if(event[i].data.fd == STDOUT_FILENO)
            {
                /*
                    这里需要清楚一个点,我们检测的是标准输出:
                    用cout,不加\n的话,缓冲区不会刷新,仍然有数据;
                */

               cout<<"LT模式,测试EPOLLOUT,不刷新缓冲区-----";

                // sleep(1);    //这里不能sleep,不然会一直阻塞住...不懂
            }
    }

    return 0;
}

结果:死循环了
非常合理,没有将写缓冲区刷新,写缓冲区有空间可写则一直触发EPOLLOUT,当buffer满的时候,buffer会自动刷清输出,同样会造成epoll_wait返回写就绪。
在这里插入图片描述



第四种:LT模式,测试EPOLLOUT,刷新缓冲区(加一个endl)

cout<<"LT模式,测试EPOLLOUT,不刷新缓冲区-----"<<endl;

结果:
同样死循环
非常合理,刷新缓冲区后,写缓冲区有空间可写,则一直触发。

在这里插入图片描述

——————————————————————————————————————————————————————

ET模式:

第五种:ET模式,测试EPOLLIN;

(1)不将读缓冲区数据读出

#include <unistd.h>
#include <iostream>
#include <sys/epoll.h>
using namespace std;

int main()
{
    int epfd = epoll_create(1); //创建epoll实例
    struct epoll_event ev,event[5];

    //设置参数
    ev.data.fd = STDIN_FILENO;  //检测输入缓冲区
    ev.events = EPOLLIN | EPOLLET;    //测试ET模式下
    epoll_ctl(epfd,EPOLL_CTL_ADD,EPOLLIN,&ev); //监听输入文件描述符

    while(1)
    {
        int nfd = epoll_wait(epfd,event,5,-1);

        for(int i=0;i<nfd;i++)
            if(event[i].data.fd == STDIN_FILENO)
            {
                cout<<"ET模式,测试EPOLLIN,不将缓冲区数据读出\n";
                // sleep(1);
            }
    }

    return 0;
}

结果:
输入一个test,输出一下,然后阻塞了,再输入再输出,然后阻塞;

分析流程:
    1. 一开始读缓冲区为空,阻塞;输入test,即将test送入读缓冲区,此时由 空 -> 不空,触发EPOLLIN。(合理,验证上面的结论)
    2. 触发EPOLLIN后,由于我们没对缓冲区处理,此时不会一直触发,即调用epoll_wait被阻塞住了。
    3. 当我们再次输入test时,读缓冲区数据增加,导致fd状态改变,此时也会触发EPOLLOUT,因此epoll_wait返回,再次输出。(合理,验证上面 读缓冲区 增加数据时 也会触发的结论)

在这里插入图片描述


(2)那么如果我们将读缓冲区的数据读出来呢?

for(int i=0;i<nfd;i++)
            if(event[i].data.fd == STDIN_FILENO)
            {
                char buff[128];
                read(STDIN_FILENO,buff,sizeof(buff));
                cout<<"ET模式,测试EPOLLIN,读出读缓冲区数据\n";
                // sleep(1);
            }

结果:
和不读出来是一样的,并不会出现死循环;
说明ET模式下,读缓冲数据 减少 / 非空->空 并不会触发EPOLLIN。
在这里插入图片描述



第六种:ET模式下,测试EPOLLIN,用 EPOLL_MOD 重置

(1)不将 读缓冲区 数据读出:

#include <unistd.h>
#include <iostream>
#include <sys/epoll.h>
using namespace std;

int main()
{
    int epfd = epoll_create(1); //创建epoll实例
    struct epoll_event ev,event[5];

    //设置参数
    ev.data.fd = STDIN_FILENO;  //检测输入缓冲区
    ev.events = EPOLLIN | EPOLLET;    //测试ET模式下
    epoll_ctl(epfd,EPOLL_CTL_ADD,STDIN_FILENO,&ev); //监听输入文件描述符

    while(1)
    {
        int nfd = epoll_wait(epfd,event,5,-1);

        for(int i=0;i<nfd;i++)
            if(event[i].data.fd == STDIN_FILENO)
            {
                cout<<"ET模式,测试EPOLLIN,用EPOLL_MOD重置,不读出缓冲区\n";

                ev.data.fd = STDIN_FILENO;  //检测输入缓冲区
                ev.events = EPOLLIN | EPOLLET;    //测试ET模式下

                epoll_ctl(epfd,EPOLL_CTL_MOD,STDIN_FILENO,&ev); //重新MOD事件(ADD无效)

                // sleep(1);
            }
    }

    return 0;
}

结果:
出现死循环(非常合理,验证了上面的结论,缓冲区非空,用EPOLL_MOD重置,会触发)

在这里插入图片描述

(2)那如果将读缓冲区数据读出来呢?

for(int i=0;i<nfd;i++)
            if(event[i].data.fd == STDIN_FILENO)
            {
                cout<<"ET模式,测试EPOLLIN,用EPOLL_MOD重置,读出读缓冲区数据\n";
                char buff[128];
                read(STDIN_FILENO,buff,sizeof(buff));
                
                ev.data.fd = STDIN_FILENO;  //检测输入缓冲区
                ev.events = EPOLLIN | EPOLLET;    //测试ET模式下

                epoll_ctl(epfd,EPOLL_CTL_MOD,STDIN_FILENO,&ev); //重新MOD事件(ADD无效)

                // sleep(1);
            }

结果:
输入一次test,输出一次,也就是说 当读缓冲区为空,用EPOLL_MOD并不会一直触发。
在这里插入图片描述



第七种:ET模式,测试EPOLLOUT;

(1)不刷新缓冲区

#include <unistd.h>
#include <iostream>
#include <sys/epoll.h>
using namespace std;

int main()
{
    int epfd = epoll_create(1); //创建epoll实例
    struct epoll_event ev,event[5];

    //设置参数
    ev.data.fd = STDOUT_FILENO;  //检测输出缓冲区
    ev.events = EPOLLOUT | EPOLLET;    //测试ET模式下,EPOLLOUT
    epoll_ctl(epfd,EPOLL_CTL_ADD,STDOUT_FILENO,&ev); //监听输出文件描述符

    while(1)
    {
        int nfd = epoll_wait(epfd,event,5,-1);

        for(int i=0;i<nfd;i++)
            if(event[i].data.fd == STDOUT_FILENO)
            {
                cout<<"ET模式,测试EPOLLOUT,不读出缓冲区";
                // sleep(1);
            }
    }

    return 0;
}

结果:
直接就阻塞了…
分析:第一次EPOLLOUT触发epoll_wait返回,然后我们往写缓冲区写数据,但是我们没刷新,此时缓冲区中有数据,此时当我们再次epoll_wait时,EPOLLOUT不触发,因此阻塞了。(非常合理,ET模式下,事件触发后不处理,下次不再触发)
在这里插入图片描述


(2)刷新缓冲区

cout<<"ET模式,测试EPOLLOUT,刷新缓冲区"<<endl;

结果:
死循环了,非常合理,验证了上面结论,ET模式下,写缓冲区数据由多变少时,会触发。
在这里插入图片描述



第八种:ET模式,测试EPOLLOUT,用EPOLL_MOD重置

(1)不刷新缓冲区

#include <unistd.h>
#include <iostream>
#include <sys/epoll.h>
using namespace std;

int main()
{
    int epfd = epoll_create(1); //创建epoll实例
    struct epoll_event ev,event[5];

    //设置参数
    ev.data.fd = STDOUT_FILENO;  //检测输出缓冲区
    ev.events = EPOLLOUT | EPOLLET;    //测试ET模式下,EPOLLOUT
    epoll_ctl(epfd,EPOLL_CTL_ADD,STDOUT_FILENO,&ev); //监听输出文件描述符

    while(1)
    {
        int nfd = epoll_wait(epfd,event,5,-1);

        for(int i=0;i<nfd;i++)
            if(event[i].data.fd == STDOUT_FILENO)
            {
                cout<<"ET模式,测试EPOLLOUT,用EPOLL_MOD重置,不刷新写缓冲区";

                ev.data.fd = STDOUT_FILENO;  //检测输入缓冲区
                ev.events = EPOLLOUT | EPOLLET;    //测试ET模式下

                epoll_ctl(epfd,EPOLL_CTL_MOD,STDOUT_FILENO,&ev); //重新MOD事件(ADD无效)
            }
    }

    return 0;
}

                 

结果:
死循环,非常合理,验证了上面的,写缓冲区可写时,用EPOLL_MOD重置后会触发EPOLLOUT
在这里插入图片描述


(2)刷新缓冲区

cout<<"ET模式,测试EPOLLOUT,用EPOLL_MOD重置,刷新写缓冲区"<<endl;

结果:
同样死循环,非常合理,刷新之后写缓冲区依然可写,EPOLL_MOD重置后,触发EPOLLOUT
在这里插入图片描述

总结

  • LT(水平触发):

    • EPOLLIN 触发条件:
         读缓冲区有数据就一直触发(即epoll_wait时能检测到),没有就不触发。

    • EPOLLOUT 触发条件:
          写缓冲区有空间可写,则一直触发。

  • ET(边缘触发)

    • EPOLLIN 触发条件:
          1. 当读 buff 从 空 -> 不空 时,触发;
          2. 当有新数据到达时,即读 buff 数据由 少 -> 多 时,触发;
          3. 当读 buff 有数据可读时,我们不处理,但是对相应fd进行epoll_mod IN事件时,触发。

    • EPOLLOUT 触发条件:
          1. 当写 buff 从 满 -> 不满 时,触发;
          2. 当有数据被送走时,即写 buff 数据由 多 -> 少 时,触发;
          3. 当写 buff 有数据,但是我们没处理(没发送出去),但是对相应fd进行epoll_mod OUT事件时,触发。

参考文章

彻底学会使用epoll(一)——ET模式实现分析

彻底学会使用epoll(二)——ET的读写操作实例分析

epoll LT/ET 深度剖析

深度剖析linux socket的epollin/epollout是何时触发的

我是一个找暑期实习的鼠鼠,今天是面完美团第二天,团子收了我吧!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/10444.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

package-cli-service,为构建发布npm包提供完整的工作流

package-cli-service 介绍 package-cli-service 是一个开发环境依赖。它是一个 npm 包&#xff0c;局部安装在每个 create-package-tools 创建的项目中。 package-cli-service 是构建于 rollup 和 webpack 之上的。它包含了&#xff1a; 一个针对绝大部分 package 优化过的内…

013 - C++引用

本期我们要讲的是 C 中的引用。 上期我们讨论了指针&#xff0c;如果你没有看过那期内容&#xff0c;你一定要回去看看&#xff0c;因为引用实际上只是指针的扩展&#xff0c;你至少需要在基本层面上理解指针是如何工作的&#xff0c;然后才能继续学习本期的内容&#xff0c;本…

MySQL索引数据结构入门

之前松哥写过一个 MySQL 系列&#xff0c;但是当时是基于 MySQL5.7 的&#xff0c;最近有空在看 MySQL8 的文档&#xff0c;发现和 MySQL5.7 相比还是有不少变化&#xff0c;同时 MySQL 又是小伙伴们在面试时一个非常重要的知识点&#xff0c;因此松哥打算最近再抽空和小伙伴们…

Golang数据类型比较

直接使用比较的情况 分类说明是否能比较说明基本类型整型&#xff08; int/uint/int8/uint8/int16/uint16/int32/uint32/int64/uint64/byte/rune等&#xff09;浮点数&#xff08; float32/float64&#xff09;复数类型&#xff08; complex64/complex128&#xff09;字符串&a…

Linux查看端口

目录 1.查看已知端口的使用情况 2.查看所有端口的占用情况 3.查看占用端口的程序的进程号 4.杀死进程号 1.查看已知端口的使用情况 #例:8080 可使用命令: netstat -anp | grep 8080 结果如下: 还可以使用这条命令: netstat -tln | grep 8080 结果如下: 区别:第一条命令后面显示…

NumPy 秘籍中文第二版:十二、使用 NumPy 进行探索性和预测性数据分析

原文&#xff1a;NumPy Cookbook - Second Edition 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 在本章中&#xff0c;我们涵盖以下秘籍&#xff1a; 探索气压探索日常气压范围研究年度气压平均值分析最大可见度用自回归模型预测气压使用移动平均模型预测气压研究年…

爱智EdgerOS之深入解析离线下载任务

一、需求分析 在日常使用计算机的过程中&#xff0c;看到喜欢的资源不可避免地想把它下载到我们的设备上保存下来&#xff0c;比如图片&#xff0c;音视频资源&#xff0c;文档资源等&#xff0c;基于这种应用场景&#xff0c;现在来看看在爱智设备上可以如何实现呢&#xff1…

76-TCP协议,UDP协议以及区别

TCP协议,UDP协议,SCTP协议一.TCP协议1.什么是TCP协议2.TCP协议的特点3.TCP头部结构4.TCP状态转移5.TCP超时重传二.UDP协议1.什么是UDP协议2.UDP协议的特点三.TCP和UDP的区别一.TCP协议 1.什么是TCP协议 TCP(Transmission Control Protocol)协议即为传输控制协议,是一种面向连…

2023-04-12 面试中常见的数组题目

数组中的问题其实最常见 通过基础问题&#xff0c;掌握写出正确算法的“秘诀”巧妙使用双索引技术&#xff0c;解决复杂问题对撞指针- 滑动窗口 1 从二分查找法看如何写出正确的程序 本节学习重点&#xff1a;处理边界问题&#xff01; 1.确定边界范围方法&#xff0c;先用区…

微服务架构-服务网关(Gateway)-权限认证(分布式session替代方案)

权限认证-分布式session替代方案 前面我们了解了Gateway组件的过滤器&#xff0c;这一节我们就探讨一下Gateway在分布式环境中的一个具体用例-用户鉴权。 1、传统单应用的用户鉴权 从我们开始学JavaEE的时候&#xff0c;就被洗脑式灌输了一种权限验证的标准做法&#xff0c;…

Elasticsearch:集群管理的一些建议

在之前的文章 “Elasticsearch&#xff1a;集群管理” &#xff0c;我们对集群管理做了一些介绍。在今天的文章中&#xff0c;我们接着来聊一下有关配置的方面的问题。这在很大程度上取决于你的用例&#xff0c;是索引还是搜索繁重。 我们将在这里讨论在集群设置方面我们需要关…

Leetcode.130 被围绕的区域

题目链接 Leetcode.130 被围绕的区域 mid 题目描述 给你一个 m x n的矩阵 board&#xff0c;由若干字符 X和 O&#xff0c;找到所有被 X围绕的区域&#xff0c;并将这些区域里所有的 O用 X填充。 示例 1&#xff1a; 输入&#xff1a;board [[“X”,“X”,“X”,“X”],[“X…

「Cpolar」使用Typecho搭建个人博客网站【内网穿透实现公网访问】

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后端的开发语言A…

在 Python 中计算两个数字之间的百分比

要计算两个数字之间的百分比&#xff0c;请将一个数字除以另一个数字&#xff0c;然后将结果乘以 100&#xff0c;例如 (30 / 75) * 100。这显示第一个数字占第二个数字的百分比。 在示例中&#xff0c;30 是 75 的 40%。 def is_what_percent_of(num_a, num_b):return (num_a…

基于SVG的HMI组件

人机界面是自动化领域不可或缺重要组成部分。人机界面系统的设计看上去并没有太大的技术门槛&#xff0c;但是设计一个HMI系统的工作量是巨大的&#xff0c;如果你没有足够的耐心和精力是难以完成一个通用HMI系统的。构建UI控件库就是一个似乎永远完不成的事情&#xff0c;用户…

Halo博客建站实战以及问题汇总

目录 简介 特性 快速开始 安装步骤 环境准备 Docker-compose方式部署 问题汇总 mac端无法访问页面 页面登录提示账号密码错误 重装注意点 资料 官方文档 简介 Halo 强大易用的开源建站工具 特性 代码开源 我们的所有代码开源在 GitHub 上且处于积极维护状态&…

《分解因数》:质因数分解

目录 一、题目&#xff1a; 二、思路&#xff1a; 三、代码&#xff1a; 一、题目&#xff1a; 分解因数 《分解因数》题目链接 所谓因子分解&#xff0c;就是把给定的正整数a&#xff0c;分解成若干个素数的乘积&#xff0c;即 a a1 a2 a3 ... an,并且 1 < a1…

HCIA第二次笔记

目录 OSI/RM七层参考模型——开放式的系统互联参考模型 核心——分层 TCP/IP模型——TCP/IP协议簇 应用层 应用层协议 封装与解封装 传输层 TCP协议和UDP协议的区别 TCP的报文 TCP的三次握手 TCP的四次挥手 TCP的四种可靠传输机制 OSI/RM七层参考模型——开放式的系…

[目标识别-论文笔记]Object Detection in Videos by Short and Long Range Object Linking

文章标题&#xff1a;2018_Cite13_Tang——Object Detection in Videos by Short and Long Range Object Linking 这篇论文也被叫做“2019_Cite91_TPAMI_Tang——Object Detection in Videos by High Quality Object Linking” 如果这篇博客对你有帮助&#xff0c;希望你 点赞…

学生信息管理系统【GUI/Swing+MySQL】(Java课设)

系统类型 Swing窗口类型Mysql数据库存储数据 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Mysql8.0Idea或eclipsejdbc 运行效果 本系统源码地址&#xff1a;https://download.csdn.net/download/qq_50954361/87673902 更多系统资源库…
最新文章