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

news2024/11/24 8:38:45

麻了,对 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.coloradmin.cn/o/418040.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

WRF-UCM 高精度城市化气象动力模拟、WRF+WRF-UCM 模拟气象场

查看原文>>>&#xff08;WRF-UCM&#xff09;高精度城市化气象动力模拟技术与案例应用 目录 模型基础理论 模型平台从零安装讲解 城市模块在线耦合&#xff08;WRFWRF-UCM&#xff09;模拟案例讲解 WRFWRF-UCM如何模拟气象场 实际应用及案例分析 其他大气相关推…

PostgreSQL插件—数据恢复工具pg_recovery使用详解

说明 pg_recovery 是一款基于PostgreSQL的数据恢复工具。针对表做了 update/delete/rollback/dropcolumn 后的数据恢复。 版本支持 pg_revovery当前支持 PostgreSQL 12/13/14 。 安装 下载插件 墨天轮下载地址&#xff1a;https://www.modb.pro/download/434516github下载地…

吃鸡录屏怎么录到自己的声音 吃鸡录屏怎么隐藏按键

很多人在玩吃鸡游戏时喜欢将自己的游戏过程录制下来&#xff0c;特别是很多游戏主播会录制视频&#xff0c;录制后将视频分享到社交平台。但是在录制时经常会遇到很多问题&#xff0c;如声音、画面清晰度和完整性等。接下来就来分享一下吃鸡录屏怎么录到自己的声音&#xff0c;…

pytorch单机多卡训练

多卡训练的方式 以下内容来自知乎文章&#xff1a;当代研究生应当掌握的并行训练方法&#xff08;单机多卡&#xff09; pytorch上使用多卡训练&#xff0c;可以使用的方式包括&#xff1a; nn.DataParalleltorch.nn.parallel.DistributedDataParallel使用Apex加速。Apex 是 N…

嵌入式学习笔记汇总

本文整理STM32、STM8和uCOS-III的所有文章链接。 STM32学习笔记目录 源码&#xff1a;mySTM32-learn STM32学习笔记&#xff08;1&#xff09;——LED和蜂鸣器 STM32学习笔记&#xff08;2&#xff09;——按键输入实验 STM32学习笔记&#xff08;3&#xff09;——时钟系统 …

.NET System.Management 获取windows系统和硬件信息

ManagementObject用于创建WMI类的实例与WINDOWS系统进行交互&#xff0c;通过使用WMI我们可以获取服务器硬件信息、收集服务器性能数据、操作Windows服务&#xff0c;甚至可以远程关机或是重启服务器。 WMI 的全称 Windows Management Instrumentation&#xff0c;即 Windows …

音视频八股文(1)--音视频基础

1.1.音视频录制原理 1.2.音视频播放原理 1.3.图像表示RGB-YUV 1.3.1 图像基础概念 ◼ 像素&#xff1a;像素是一个图片的基本单位&#xff0c;pix是英语单词picture的简写&#xff0c;加上英 语单词“元素element”&#xff0c;就得到了“pixel”&#xff0c;简称px&#xff…

使用Mingw64在CLion中搭建Linux开发环境

1.前言&#xff1a; 博主本来一直是在Visual Studio 2017中使用C语言编写程序&#xff0c;但有个问题是Visual Studio 2017默认使用自带的Windows SDK和编译器&#xff0c;我想使用POSIX文件操作就不行&#xff08;因为Windows中没有Linux SDK&#xff09;&#xff0c;虽然Wind…

【Kafka-架构及基本原理】Kafka生产者、消费者、Broker原理解析 Kafka原理流程图

【Kafka-架构及基本原理】Kafka生产者、消费者、Broker原理解析 & Kafka原理流程图1&#xff09;Kafka原理1.1.生产者流程细节1.2.Broker 的存储流程细节1.3.消费者流程细节2&#xff09;Kafka读写流程图1&#xff09;Kafka原理 1.1.生产者流程细节 1、生产者发送消息到 …

计算机毕业设计源码整合大全_kaic

以下为具体单个列表&#xff08;单个下载在我主页搜索即可&#xff09;&#xff1a; 1&#xff1a;计算机专业-ASP(499套&#xff09; ASP学生公寓管理系统的设计与实现(源代码论文).rar 1&#xff1a;计算机专业-ASP(499套&#xff09; ASP学科建设设计(源代码论文).ra…

Clickhouse 引擎之MergeTree详解

分区详解 数据存储底层分布 # 数据在这个位置 rootfjj001:~# cd /var/lib/clickhouse/data rootfjj001:/var/lib/clickhouse/data# ls # 数据库 default system rootfjj001:/var/lib/clickhouse/data# cd default/ rootfjj001:/var/lib/clickhouse/data/default# ls #表 enu…

ASEMI代理AD8400ARZ10-REEL原装ADI车规级AD8226ARZ-R7

编辑&#xff1a;ll ASEMI代理AD8400ARZ10-REEL原装ADI车规级AD8226ARZ-R7 型号&#xff1a;AD8400ARZ10-REEL 品牌&#xff1a;ADI/亚德诺 封装&#xff1a;SOIC-8 批号&#xff1a;2023 引脚数量&#xff1a;8 安装类型&#xff1a;表面贴装型 AD8400ARZ10-REEL汽车芯…

Zabbix监控系统——附详细步骤和图解

文章目录一、Zabbix概述1、使用zabbix的原因2、zabbix的概念和构成3、zabbix 监控原理&#xff1a;4、zabbix的程序组件二、安装 zabbix 5.01、部署 zabbix 服务端的操作步骤2、实例操作&#xff1a;部署 zabbix 服务端3、部署 zabbix 客户端4、实例操作&#xff1a;部署 zabbi…

【Linux】揭开套接字编程的神秘面纱(下)

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《学会Linux》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;前言&…

(二十三)槽函数的书写规则导致槽函数触发2次的问题

在创建QT的信号和槽时&#xff0c;经常无意间保留着QT书写槽函数的习惯&#xff0c;或者在QT设计界面直接右键【转到槽】去创建槽函数&#xff0c;但是后期需要用到disconnect时&#xff0c;又重新写了一遍connect函数&#xff0c;那么你会发现实际槽函数执行了2遍。 首先来看…

要在Ubuntu中查找进程的PID,可以使用pgrep或pidof命令。

一 查找进程 1.pgrep命令 pgrep命令可以根据进程名或其他属性查找进程的PID。例如&#xff0c;要查找名为"firefox"的进程的PID&#xff0c;可以在终端中输入以下命令&#xff1a; pgrep firefox如果有多个名为"firefox"的进程&#xff0c;pgrep命令将返…

互联网一个赛道只剩下几家,真要爆品

互联网一个赛道剩下几家&#xff0c;真要爆品 2017年的书&#xff0c;案例基本上是马后炮总结 趣讲大白话&#xff1a;说起来容易&#xff0c;做起来难 【趣讲信息科技136期】 **************************** 书中讲的范冰冰翻车了 书中不看好的线下渠道&#xff0c;现在成香饽饽…

面试篇-Java并发之CAS:掌握原理、优缺点和应用场景分析,避免竞态问题

1、CAS介绍及原理 多线程中的CAS&#xff08;Compare-and-Swap&#xff09;操作是一种常见的并发控制方法&#xff0c;用于实现原子性更新共享变量的值。其核心思想是通过比较内存地址上的值和期望值是否相等来确定是否可以进行更新操作&#xff0c;从而避免多线程条件下的竞态…

HMI实时显示网络摄像机监控画面——以海康威视网络摄像机为例

随着IOT技术的快速发展&#xff0c;网络摄像机快速应用于工业领域&#xff0c;结合其他智能设备建立一个智能系统&#xff0c;提高用户与机器设备之间的交互体验&#xff0c;帮助企业优化人员配置。 作为重要的可视化设备&#xff0c;HMI不仅可以采集现场设备数据&#xff0c;…

uniapp系列-使用uniapp携带收件人信息调用手机邮件应用发邮件的2种方案

背景描述 我们使用uniapp打包之后&#xff0c;某些情况下&#xff0c;需要使用uniapp打开手机其他应用去发邮件&#xff0c;携带对方email 信息以及主题信息等&#xff0c;那我们应该怎么处理呢&#xff1f; 方案一&#xff1a;使用uniapp标签-uni-link&#xff0c;注意这种方…