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一次只处理一个连接,防止漏掉连接,选择水平触发。

目录
相关文章
|
7月前
|
监控 网络协议 大数据
epoll中的ET和LT模式区别
epoll中的ET和LT模式区别
126 0
|
7月前
|
网络协议
Epoll事件ET和LT模型分析
Epoll事件ET和LT模型分析
77 0
|
3月前
|
监控 网络协议 Linux
彻底解密:select,poll底层系统调用的核心思想原理
彻底解密:select,poll底层系统调用的核心思想原理
|
5月前
|
存储 Java Unix
(八)Java网络编程之IO模型篇-内核Select、Poll、Epoll多路复用函数源码深度历险!
select/poll、epoll这些词汇相信诸位都不陌生,因为在Redis/Nginx/Netty等一些高性能技术栈的底层原理中,大家应该都见过它们的身影,接下来重点讲解这块内容。
|
6月前
|
监控 Linux
LT模式下epoll一直通知可写怎么办?
LT模式下epoll一直通知可写怎么办?
48 1
|
6月前
|
数据处理
epoll的水平触发(LT)和边缘触发模式(ET)详解
epoll的水平触发(LT)和边缘触发模式(ET)详解
303 0
|
存储 安全 编译器
大师学SwiftUI第9章Part 2 - 异步并发之Actor、异步序列、任务组和异步图像
异步任务对于希望释放资源让系统可以执行其它任务的场景非常有用,比如更新界面,但在希望同步执行两个任务时,就需要用到并发。为此,Swift标准库定义了async let语句。
130 2
|
7月前
|
Linux
Linux网络编程(epoll的ET模式和LT模式)
Linux网络编程(epoll的ET模式和LT模式)
170 0
|
NoSQL 应用服务中间件 Linux
Epoll的本质(内部实现原理)
Epoll的本质(内部实现原理)
211 0
Epoll的本质(内部实现原理)
|
缓存 网络协议 程序员
举源码实例来说明epoll之LT和ET模式的区别
举源码实例来说明epoll之LT和ET模式的区别
151 0