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

相关推荐
Mr YiRan3 小时前
网络请求API监控与网络切换埋点
android·网络
初願致夕霞4 小时前
C++11:类型推导与完美转发复盘笔记
c++
小猪写代码4 小时前
C++中什么是类,什么是对象
c++
bosins4 小时前
彻底解决 WSL 2.7+ 空闲自动关闭/内存回收导致 Docker 中断的问题
linux·docker·wsl·dashboard
LongRunning4 小时前
【Linux】H3网络(二)
linux
国际云,接待5 小时前
华为云 EVS 性能诊断:用 CES、ECS QoS 与 fio 定位 IOPS 和吞吐瓶颈
服务器
xx~t6 小时前
嵌入式学习——ARM1
linux·arm开发·嵌入式硬件·arm
xx~t6 小时前
嵌入式——ARM1(详细篇)
linux·arm开发·嵌入式硬件·arm
我要见SA姐16 小时前
告别 Copilot?Codex 本地化部署指南
运维·数据库·机器学习·oracle·回归
Elastic 中国社区官方博客7 小时前
列式存储并不等同于列式数据库。Columnar 模式为 Elasticsearch 带来了什么
大数据·运维·数据库·elasticsearch·搜索引擎