epoll的水平触发LT以及边沿触发ET的原理及使用及优缺点

简介: epoll的水平触发LT以及边沿触发ET的原理及使用及优缺点

epoll的水平触发LT以及边沿触发ET的原理及使用及优缺点

网络编程相关文章:

select、poll、epoll、多线程实现并发请求处理

epoll-reactor模型原理代码解析

Http解析实现/服务器Get请求的实现


在IO多路复用的几种方法中,select和poll只支持水平触发,而epoll支持水平触发和边缘触发两种形式,因此在并发网络编程中该如何选择哪种触发方式呢?(epoll的原理不清楚的可以看一下这篇文章link

水平触发和边缘触发的不同会影响epoll的事件通过epoll_wait函数的响应。

水平触发(LT):只要缓冲区有数据,epoll_wait就会一直被触发,直到缓冲区为空;(有数据会连续触发)

边沿触发(ET):只有所监听的事件状态改变或者有事件发生时,epoll_wait才会被触发;(有数据只触发一次)

水平触发和边沿触发怎么使用呢?

LT和ET的代码案例:

首先介绍下epoll涉及的函数

int epoll_create(int size);//创建一个监听红黑树,并且返回红黑树的根节点  失败:-1,设置errno
int epoll_ctl(int epfd, int op, int fd, struct epoll_event* event);//对该监听红黑树所做的操作
//总共三个操作可选
//EPOLL_CTL_ADD 添加fd到监听红黑树
//EPOLL_CTL_MOD 修改fd在监听红黑树上的监听事件
//EPOLL_CTL_DEL 将一个fd从监听红黑树上取下(取消监听)
int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout)//监听文件描述符

水平触发LT

int epollfd = epoll_create( 5 );//创建epoll对象,epollfd是保存文件描述符的红黑树根节点
  epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLLT;//添加EPOLLLT事件
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);//通过epoll_ctl函数添加监听的fd

边沿触发ET

int epollfd = epoll_create( 5 );//创建epoll对象,epollfd是保存文件描述符的红黑树根节点
  epoll_event event;
    event.data.fd = fd;
    event.events = EPOLLIN | EPOLLET;//添加EPOLLET事件
    epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);//通过epoll_ctl函数添加监听的fd

使用水平触发LT或边沿触发ET的结果及处理

开始说了,不同的触发方式会影响epoll_wait函数,那该怎么处理呢?

水平触发LT

//LT
int number = epoll_wait( epollfd, events, MAX_EVENT_NUMBER, -1 );//通过epoll_wait监听到epoll事件响应,events中会保存响应的事件队列
sockfd = events[i].data.fd;//从事件队列中取出对应的文件描述符fd
n = recv(sockfd , buff, MAXLNE, 0);//通过recv将sockfd缓冲区的数据接收进buff中

水平触发写到上面就可以实现接收数据了,当buff中接收满了时,如果sockfd中假如还有数据没传完,不用担心。水平触发LT会继续触发EPOLLIN事件,epoll_wait函数会再次响应该事件,再来继续接收数据。这就是水平触发(有数据会连续触发)的意思。

边沿触发ET

//ET
int number = epoll_wait( epollfd, events, MAX_EVENT_NUMBER, -1 );//通过epoll_wait监听到epoll事件响应,events中会保存响应的事件队列
sockfd = events[i].data.fd;//从事件队列中取出对应的文件描述符fd
m_read_idx = 0;//记录一次读数据后的位置索引
while(true) {//
   // 从buff + m_read_idx索引出开始保存数据
    n = recv(sockfd , buff + m_read_idx, MAXLNE, 0 );//通过recv将sockfd缓冲区的数据接收进buff中
    if (n == -1) {
        if( errno == EAGAIN || errno == EWOULDBLOCK ) {
            // 没有数据
            break;
        } 
    } else if (n == 0) {   // 对方关闭连接
    //close(sockfd);
    break;
    }
    m_read_idx += n;
}

可以看出边沿触发ET和水平触发在读取数据时有点不同。

因为边沿触发ET(有数据只触发一次),所以假如一次recv系统调用无法将sockfd的数据全部读完的话,水平触发不会再触发EPOLLIN事件,epoll_wait函数也不会u对这一个sockfd做出响应。所以当使用ET做触发模式时,我们通常使用while()循环来将sockfd的数据全部读出来。上面程序通过m_read_idx索引记录每次读完后的位置也是常用的方法。

水平触发和边沿触发的优缺点

水平触发LT

优点:程序简单,会完整地读取所有数据。

缺点:重复地事件触发会影响高并发服务器地性能,因为epoll监控事件涉及到系统调用,需要用户态-内核态的转换。LT消耗了大量的系统资源,影响服务器性能;

边沿触发ET

优点:每次epoll_wait只用触发一次,通过程序逻辑实现读取缓冲区的所有数据,工作效率高,大大提升了服务器性能;

缺点(没归纳出来,随便写一个):不能保证数据的完整。(这个可以通过上面提到的程序逻辑实现完整地读取数据)

边沿触发没什么缺点?那是不是用epoll就用ET边沿触发就好了?

我的理解是, YES。在日常用epoll实现并发处理,可以优先使用“边沿触发(EPOLLET)+非阻塞IO”模式。

高并发服务器边沿触发(ET) 的效率更高

因为边沿触发只在数据到来的一刻才触发,很多时候服务器在接受大量数据时会先接受数据头部(水平触发在此触发第一次,边沿触发第一次)。

接着服务器通过解析头部决定要不要接这个数据。此时,如果不接受数据,水平触发需要手动清除(水平触发当有数据时,会一直触发,直到没有数据可读),而边沿触发可以将清除工作交给一个定时的清除程序去做(只触发一次,不需要的数据可以不读),自己立刻返回。

但是如果sockfd中发送的数据较小,我一次recv就能全部读完,这样LT也不会重复触发epoll事件,和边沿触发的性能差不多,那我们为什么不用更简单的水平触发呢,当然可以使用。那其实就可以理解水平触发和边沿触发是有一个分界点,就是看sockfd的数据是小数据还是大数据。recv的BUFFER_LENGTH如果一次能接收完recv buffer中的数据,就是小数据,一次接收不完就是大数据。小数据就用水平触发,大数据就用边沿触发

但但但但是LT还在一种场景有使用,就是Nginx服务器中listenfd是用的水平触发 。(网络服务器中一般涉及两类sockfd,一种是用于监听是否有连接请求的socket,一种是用于传输数据的socket(上面讲的sockfd都是用于传输数据的))

有一些解释是listenfd用水平触发,如果多个client同时连接进来,listenfd里面积攒多个连接的话,accept一次只处理一个连接,防止漏掉连接,选择水平触发。

目录
相关文章
|
缓存 NoSQL Java
面试官:如何保证本地缓存的一致性?
面试官:如何保证本地缓存的一致性?
2450 1
|
SQL 缓存 算法
CPU密集型和IO密集型任务的权衡:如何找到最佳平衡点
CPU密集型与I/O密集型是在计算机上执行任务的两种策略,在并发执行任务场景下,我们需要选择使用多线程或多进程; 如果是IO密集型任务,使用多线程,线程越多越好; 如果是CPU密集型任务,使用多进程,线程数量与CPU核心数匹配。
1710 0
|
存储 缓存 NoSQL
Leveldb学习笔记:leveldb的使用与原理探究
Leveldb学习笔记:leveldb的使用与原理探究
Leveldb学习笔记:leveldb的使用与原理探究
|
Ubuntu
Ubuntu 20.04 多网卡路由规则配置
Ubuntu 20.04 多网卡路由规则配置
5207 0
|
安全 算法 API
OpenSSL支持哪些加密算法?
【10月更文挑战第4天】OpenSSL支持哪些加密算法?
868 5
|
数据处理
epoll的水平触发(LT)和边缘触发模式(ET)详解
epoll的水平触发(LT)和边缘触发模式(ET)详解
649 0
|
7月前
|
人工智能 供应链 搜索推荐
《深度融合:工业互联网架构与人工智能驱动智能制造新变革》
在全球制造业数字化、网络化、智能化的浪潮中,工业互联网网络架构与人工智能的融合成为智能制造的关键路径。工业互联网作为智能制造的基石,连接人、机器、车间等主体,实现全要素互联;人工智能则为其注入智慧引擎,带来自主学习、分析决策和优化能力。二者融合不仅重塑了生产模式,还开启了制造业创新发展的新篇章,助力企业实现高效生产、个性化定制和供应链协同管理。尽管面临技术、人才等挑战,但通过协同创新,智能制造正逐步变为现实,塑造未来工业新格局。
217 8
|
7月前
|
数据库 C++
【数据结构进阶】红黑树超详解 + 实现(附源码)
本文深入探讨了红黑树的实现原理与特性。红黑树是一种自平衡二叉搜索树,通过节点着色(红/黑)和特定规则,确保树的高度接近平衡,从而实现高效的插入、删除和查找操作。相比AVL树,红黑树允许一定程度的不平衡,减少了旋转调整次数,提升了动态操作性能。文章详细解析了红黑树的性质、插入时的平衡调整(变色与旋转)、查找逻辑以及合法性检查,并提供了完整的C++代码实现。红黑树在操作系统和数据库中广泛应用,其设计兼顾效率与复杂性的平衡。
1271 3
|
11月前
|
消息中间件 监控 NoSQL
Redis脑裂问题详解及解决方案
Redis脑裂问题是分布式系统中常见的复杂问题,合理配置Redis Sentinel、使用保护模式、采用分布式锁机制以及优化网络和客户端连接策略等措施,可以有效预防和解决脑裂问题。通过深入理解Redis脑裂问题的成因和影响,采取相应的解决方案,能够提高系统的可用性和数据一致性,保障Redis集群的稳定运行。希望本文能帮助你更好地理解和应对Redis脑裂问题。
930 2
|
算法 安全 Java
三种方法教你实现多线程交替打印ABC,干货满满!
本文介绍了多线程编程中的经典问题——多线程交替打印ABC。通过三种方法实现:使用`wait()`和`notify()`、`ReentrantLock`与`Condition`、以及`Semaphore`。每种方法详细讲解了实现步骤和代码示例,帮助读者理解和掌握线程间的同步与互斥,有效解决并发问题。适合不同层次的开发者学习参考。
758 11