当前位置: 首页 > news >正文

Linux高性能服务器之I/O复用之实例 ET AND LT(图像理解)(14)

LT和ET模式

前言

epoll对文件描述符的操作有两种模式: LT (Level Trigger,电平触发〉模式和ET(EdgeTrigger,边沿触发〉模式。LT模式是默认的工作模式,这种模式下epoll相当于一个效率较高的poll。当往epoll内核事件表中注册一个文件描述符上的EPOLLET事件时,epoll将以ET模式来操作该文件描述符。ET模式是epoll的高效工作模式。

对于采用LT工作模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件。这样,当应用程序下一次调用epoll_wait时,epoll_wait还会再次向应用程序通告此事件,直到该事件被处理。

而对于采用ET工作模式的文件描述符,当epoll_wait检测到其上有事件发生并将此事件通知应用程序后,应用程序必须立即处理该事件,因为后续的epoll_wait调用将不再向应用程序通知这一事件。可见,ET模式在很大程度上降低了同一个epoll事件被重复触发的次数,因此效率要比LT模式高。

(部分)实验代码之LT与ET差别

    #include<sys/socket.h>
    #include<sys/types.h>
    #include<netinet/in.h>
    #include<arpa/inet.h>
    #include<assert.h>
    #include<stdio.h>
    #include<unistd.h>
    #include<stdlib.h>
    #include<fcntl.h>
    #include<string.h>
    #include<errno.h>
    #include<sys/epoll.h>
    #include<pthread.h>
    
    #define MAX_EVENT_NUMBER 10
    #define BUFFER_SIZE   1024
    /*
    parameter meaning
    fd        文件描述符
    function performance:
    将文件描述符设置成非阻塞
    function idea:
    从一个模板出去 可以不改变一个文件描述符的特性
    */
    int setnoblocking(int fd)
    {
        int old_option=fcntl(fd,F_GETFD);//得到属性
        int new_option=old_option|O_NONBLOCK;
        fcntl(fd,F_SETFD,new_option);
        return old_option;
    }

    /*
    parameter        meaning
    epollfd          epoll事件表唯一地址
    fd               文件描述符
    enable_et        是否启用ET模式
    function performance:
    将文件描述符fd上的EPOLLIN注册到epollfd上的epoll内核事件表上
    参数enable_et指定fd是否启用ET模式
    */ 
     
    void addfd(int epollfd,int fd,bool enable_et)
    {
        struct epoll_event event;
        event.data.fd=fd;//bind fd
        event.events=EPOLLIN;//普通数据可读
        if(enable_et)
        {
            event.events|=EPOLLET;
        }
        epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&event);//将fd上的事情注册到epollfd上
        setnoblocking(fd);
    }

 /*
    parameter   meaning
    events      注册表
    number      用户数据 fd的数量
    function proformance:
    LT工作模式
    */
    void lt(epoll_event* events,int number,int epollfd,int sockfd)
    {
        char buf[BUFFER_SIZE];
        for(int i=0;i<number;i++)
        {
            int fd=events[i].data.fd;
            if(fd==sockfd)
            {
                struct sockaddr_in client;
                socklen_t client_length=sizeof(client);
                int connfd=accept(sockfd,(struct sockaddr*)&client,&client_length);
                addfd(epollfd,sockfd,false);
    
            }
            else if(events[i].data.fd&EPOLLIN)
            {  
                printf("event trigger once\n");
                memset(buf,'\0',BUFFER_SIZE);
                int ret=recv(sockfd,buf,BUFFER_SIZE-1,0);
                if(ret<=0)
                {
                    close(sockfd);
                    continue;
                }
                 printf("get %d bytes pg content %s\n",ret,buf);
                
            }
            else 
            { printf("something else happened\n");

            }
        }
    }
    
    /*
    parameter   meaning
    events      注册表
    number      用户数据 fd的数量
    function proformance:
    ET工作模式
    */
    void et(epoll_event* events,int number,int epollfd,int sockfd)
    {
       char buf[BUFFER_SIZE];
       int i;
       for(i=0;i<number;++i)
       {
         int fd=events[i].data.fd;//触发
         if(fd==sockfd)
         {
           struct sockaddr_in client;
           socklen_t client_length=sizeof(client);
           int connfd=accept(fd,(struct sockaddr*)&client,&client_length);
           addfd(epollfd,connfd,true);
           
         }
         else if(events[i].events&EPOLLIN)
         {
            /*/*这段代码不会被重复触发,所以我们循环读取数据,
            以确保把socket读缓存中的所有数据读出*/
            printf("event trigger once\n");
            while(1)
            {
                memset(buf,'\0',BUFFER_SIZE);
                int ret=recv(sockfd,buf,BUFFER_SIZE-1,0);
                if(ret<0)
                {
                    /*/*对于非阻塞Io,下面的条件成立表示数据已经全部读取
                    完毕。此后,epol1就能再次触发sockfd 上的EPOLLIN事件,
                    以驱动下一次读操作*/
                    if((errno==EAGAIN)||(errno==EWOULDBLOCK))
                    {
                        printf("read later\n");
                        break;
                    }
                    close(fd);
                    break;
                    
                }
                else if(ret==0)
                {
                    close(fd);
                }
                else
                {
                    printf("get %d bytes pg content %s\n",ret,buf);
                }
            }

         }
         else
         {
            printf("something else happened\n");
         }
       }
    }


    int main(int argc,char* argv[])
    {
        if(argc<=2)
        {
            printf("Usage :%s ip_address port_number\n",basename(argv[0]));
            return 1;
        }

        char* ip=argv[1];
        int port=atoi(argv[2]);
         
         int ret=0;
         struct sockaddr_in address;
         bzero(&address,sizeof(address));
         address.sin_family=AF_INET;
         address.sin_port=htons(port);
         inet_pton(AF_INET,ip,&address.sin_addr);

         int sockfd=socket(PF_INET,SOCK_STREAM,0);
         assert(sockfd!=-1);

         ret=bind(sockfd,(struct sockaddr*)&address,sizeof(address));
         assert(ret!=-1);

         ret=listen(sockfd,5);
         assert(ret-1);

         struct epoll_event event[MAX_EVENT_NUMBER];
         int epollfd=epoll_create(5);
         assert(epollfd!=-1);
         addfd(epollfd,sockfd,true);
         
         while(1)
         {
            int ret=epoll_wait(epollfd,event,MAX_EVENT_NUMBER,-1);
            if(ret<0)
            {
                printf("epoll failure\n");
                break;
            }
            lt(event,ret,epollfd,sockfd);
            
         }
         close(sockfd);
         return 0;

        
    }

程序流图

                                                               LT工作流图                                                                                                              

           

  ET工作流图

答疑:

对于非阻塞Io,下面的条件成立表示数据已经全部读取 完毕。此后,epol1就能再次触发sockfd 上的EPOLLIN事件, 以驱动下一次读操作?

 当应用程序在socket中设置O_NONBLOCK属性后,如果发送缓存被占满,send就会返回EAGAIN或EWOULDBLOCK 的错误。在将socket设置O_NONBLOCK属性后,通过socket发送一个100K大小的数据,第一次成功发送了13140数据,之后继续发送并未成功,errno数值为EAGAIN错误。
 

切记:

注意︰每个使用ET模式的文件描述符都应该是非阻塞的。如果文件描述符是阻塞的,那么读或写操作将会因为没有后续的事件而一直处于阻塞状态(饥渴状态)。
 

相关文章:

  • 计算机毕业设计django基于python大学生心理健康系统(源码+系统+mysql数据库+Lw文档)
  • java计算机毕业设计个性化推荐的扬州农业文化旅游管理平台源码+数据库+系统+lw文档+mybatis+运行部署
  • C#进阶03——常用泛型数据结构类
  • 第4章Linux实操篇-远程登录到Linux服务器
  • 【FPGA教程案例66】硬件开发板调试6——基于FPGA的UDP网口通信和数据传输
  • 第2章Linux基础篇-VM和Linux的安装
  • [NOI2022] 众数 题解
  • linux文件IO
  • 自已定义一个Java异常——子定义异常,和异常遇到的面试题。
  • 计算机视觉+人工智能面试笔试总结——目标检测/图像处理基础题
  • 【html】面试问题总结
  • Python:for循环语句
  • 第9章Linux实操篇-组管理和权限管理
  • java计算机毕业设计基于安卓Android/微信小程序的产后康复APP
  • 计算机毕业设计ssm图书馆管理系统z3z90系统+程序+源码+lw+远程部署
  • 【跃迁之路】【463天】刻意练习系列222(2018.05.14)
  • 30天自制操作系统-2
  • Fabric架构演变之路
  • JavaScript 奇技淫巧
  • k8s如何管理Pod
  • Mac 鼠须管 Rime 输入法 安装五笔输入法 教程
  • 如何邀请好友注册您的网站(模拟百度网盘)
  • 深入 Nginx 之配置篇
  • 想使用 MongoDB ,你应该了解这8个方面!
  • 一个SAP顾问在美国的这些年
  • 用简单代码看卷积组块发展
  • 在weex里面使用chart图表
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • ​油烟净化器电源安全,保障健康餐饮生活
  • #if #elif #endif
  • (1)安装hadoop之虚拟机准备(配置IP与主机名)
  • (8)STL算法之替换
  • (Matalb回归预测)PSO-BP粒子群算法优化BP神经网络的多维回归预测
  • (Python第六天)文件处理
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)python房屋租赁管理系统 毕业设计 745613
  • (三十五)大数据实战——Superset可视化平台搭建
  • (算法)Game
  • .NET delegate 委托 、 Event 事件,接口回调
  • .NET Micro Framework初体验(二)
  • @EnableConfigurationProperties注解使用
  • [ vulhub漏洞复现篇 ] Django SQL注入漏洞复现 CVE-2021-35042
  • [3D游戏开发实践] Cocos Cyberpunk 源码解读-高中低端机性能适配策略
  • [BUUCTF]-Reverse:reverse3解析
  • [bzoj4010][HNOI2015]菜肴制作_贪心_拓扑排序
  • [C]整形提升(转载)
  • [C++]18:set和map的使用
  • [Codeforces] number theory (R1600) Part.11
  • [ERROR] ocp-server-ce-py_script_start_check-4.2.1 RuntimeError: ‘tenant_name‘
  • [i.MX]飞思卡尔IMX6处理器的GPIO-IOMUX_PAD说明
  • [Power Query] 数据的拆分、提取与合并
  • [Typescript] tsconfig.json项目配置说明
  • [UE4]创建自定义AIController的方法(C++)
  • [UE4]性能优化指南(程序向)
  • [Vulnhub]靶场 Red