ET和LT详解

引用

本篇文章是最后一篇关于《Linux高性能服务器编程》的总结,我把这篇文章放在最后是因为我想通过这本书中大部分的项目实战总结出一些关于ET和LT的理解。

简介

水平触发,这个是默认的工作模式,如果我们不作任何的设置,就默认是水平触发。采用LT的文件描述符,当epoll_wait检测到其上有事件发生的时候,通知应用程序,应用程序可以不立即处理。则样子当应用程序下一次调用epoll_wait时,还可以继续通知,直到该事件被处理。而ET的文件描述符,应用程序必须立刻处理该事件,因为后续的epoll_wait不会再次通知该事件,可见ET更加的高效,因为只会通知一次,不会多次触发epoll。

LT

作为LT,我们不需要while()循环来读取数据,因为我们如果有数据没有读完,我们可以等待下一轮的通知。就是在for()循环里面,我们对每一次的事件都只处理了一次,不管处理完了没有,如果处理完了,那么这个事件就结束了,如果没有处理完,那么这个事件就会加入下一次的epoll,当for()结束了之后,epoll又会再一次的通知,直到没有数据可以读。这个效率其实比较低,因为不断的通知,会有内核到用户的切换,性能低。

cpp 复制代码
// LT工作流程
void lt (epoll_event* events, int number, int epollfd, int listenfd) {
    char buf[BUFFER_SIZE];
    for (int i = 0; i < number; i++) {
        int sockfd = events[i].data.fd;
        if (sockfd == listenfd) {
            sockaddr_in clientAddress;
            socklen_t len = sizeof(clientAddress);
            int connfd = accept(listenfd, (sockaddr*)&clientAddress, &len);
            addFd(epollfd, connfd, false);
        }
        else if (events[i].events & EPOLLIN) {
            // 只要socket缓存中还有未读出的数据,这段代码就被触发
            memset(buf, '\0', sizeof(buf));
            int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);
            if (ret <= 0) {
                close(sockfd);
                continue;
            }
        }
        else {
            /* code */
        }
    }
}

ET

我们ET,因为只会通知一次事件,所以一旦通知了,就必须处理完,否则这个事件就不会再一次处理了。所以我们用while()来处理事件。然后因为ET不是默认的,所以我们还要设置文件描述符的性质时ET

cpp 复制代码
// et的工作流程
void et (epoll_event* events, int number, int epollfd, int listenfd) {
    char buf[BUFFER_SIZE];
    for (int i = 0; i < number; i++) {
        int sockfd = events[i].data.fd;
        if (sockfd == listenfd) {
            sockaddr_in clientAddress;
            socklen_t len = sizeof(clientAddress);
            int connfd = accept(listenfd, (sockaddr*)& clientAddress, &len);
            addFd(epollfd, connfd, true);
        }
        else if (events[i].events & EPOLLIN) {
            // 这段代码不会重复触发,所以我们要循环读取数据,以确保把socket读缓存里面的数据全部读完
            while(1) {
                memset(buf, '\0', BUFFER_SIZE);
                int ret = recv(sockfd, buf, BUFFER_SIZE - 1, 0);
                if (ret < 0) {
                    // 对于非阻塞IO,下面条件成立表示数据已经全部读取完毕,此后,epoll就可以被再一次触发sockfd上的EPOLLIN事件,以驱动下一次读操作
                    if (errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        close(sockfd);
                        break;
                    }
                    else if (ret == 0) {
                        close(sockfd);
                    }
                    else {
                        std::cout << buf << std::endl;
                    }
                }
                else {
                    std::cout <<  "something else happened" << std::endl;
                }
            }
        }
    }
}

其实这两个并没有好坏之分,LT的性能虽然低,但是可以保证每一个事件都可以得到及时的处理,一旦一个事件读取或者写入的时间比较长,而我们选择了ET,那么一个事件就会卡特别久,导致其他事件根本没有被处理。但是如果每个事件的处理时间不长,那么选择ET是一个比较好的选择,可以避免系统在内核和用户之间的切换。

总结

本系列《Linux高性能服务器编程》的内容就到这里结束了!!!如果大家能看到这里,十分的感谢大家的陪伴。真心希望可以帮助大家理解,同时也监督我自己的学习~~~~

相关推荐
在角落发呆4 小时前
工具配置备份步骤:从手动备份到自动化方案
运维·windows·自动化
fpcc4 小时前
c++编程实践—堆和栈越界调试
c++
_upupup5 小时前
Linux的调试工具——gdb/cgdb
linux·服务器
百度一下吧5 小时前
Nginx 使用手册:从安装配置到实战部署
运维·nginx
JeJe同学5 小时前
Docker镜像导入、容器启动
linux·运维·docker
木子n15 小时前
车载以太网与SOME/IP服务化通信实战-01.从字节到服务:SOME/IP报文解析与四通道开销账
网络·网络协议·tcp/ip·车载以太网·some/ip
Jason_zhao_MR5 小时前
Linux与实时控制如何兼得
linux·嵌入式硬件·机器人·工业控制
回眸&啤酒鸭5 小时前
【回眸】自动化白盒测试落地实战指南
运维·自动化·log4j
weiabc6 小时前
MSYS2 UCRT64 + g++ C++ 中文输出乱码 完整全过程
c++
闲云野鹤在人间6 小时前
MySQL|从理论、安装、备份到主从复制、MHA高可用详解
linux·运维·数据库·mysql·云计算