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

相关推荐
田径他爸来了17 分钟前
嵌入式学习第32天(同时多个用户访问的服务器)
运维·服务器·学习
wefg119 分钟前
C++ 仿 muduo 库实现高并发服务器
运维·服务器
skr爱码士21 分钟前
10_Qt Designer 与 UI 文件(.ui 原理、uic 编译、动态加载)
c++·qt·ui·客户端
深圳市宝华视联25 分钟前
移动示教推车功能介绍
网络·嵌入式硬件·音视频·视频编解码·嵌入式实时数据库
梦難27 分钟前
linux的系统命令
linux·服务器·网络
Java小白笔记29 分钟前
Java中PDF文件导出,生成链路与实现
服务器·网络·oracle
跨境技工小黎31 分钟前
YouTube联盟营销如何变现?如何利用IP代理提高流量变现效果?
服务器·网络·tcp/ip
瓦学妹37 分钟前
HTTP代理性能分析:如何利用 IPFoxy 快速灵活切换协议?
网络·网络协议·http
Behaviour39 分钟前
Unity 手游网络同步技术
网络·unity·c#·游戏引擎