彻底学会使用epoll(五)—— ET模式下的注意事项

epoll的LT和ET模式 epoll 对文件描述符有两种操作模式:LT(Level Trigger,电平触发)模式和 ET(EdgeTrigger,边沿触发)模式。LT 模式是默认的工作模式。当往 epoll 内核事件表中注册一个文件描述符上的 EPOLLET 事件时,epoll 将以高效的 ET 模式 来操作该文件描述符。对于 LT 模式操作的文件描述符,当 epoll_wait 检测到其上有事件发生并将此事件通知应用程序后,应用程序可以不立即处理该事件。 阅读详情

5.1 ET模式下的读写

    经过前面几节分析,我们可以知道,当epoll工作在ET模式下时,对于读操作,如果read一次没有读尽buffer中的数据,那么下次将得不到读就绪的通知,造成buffer中已有的数据无机会读出,除非有新的数据再次到达。对于写操作,主要是因为ET模式下fd通常为非阻塞造成的一个问题——如何保证将用户要求写的数据写完。

要解决上述两个ET模式下的读写问题,我们必须实现:

a. 对于读,只要buffer中还有数据就一直读;

b. 对于写,只要buffer还有空间且用户请求写的数据还未写完,就一直写。

要实现上述a、b两个效果,我们有两种方法解决。

l 方法一

(1) 每次读入操作后(read,recv),用户主动epoll_mod IN事件,此时只要该fd的缓冲还有数据可以读,则epoll_wait会返回读就绪。

(2) 每次输出操作后(write,send),用户主动epoll_mod OUT事件,此时只要该该fd的缓冲可以发送数据(发送buffer不满),则epoll_wait就会返回写就绪(有时候采用该机制通知epoll_wai醒过来)。

这个方法的原理我们在之前讨论过:当buffer中有数据可读(即buffer不空)且用户对相应fd进行epoll_mod IN事件时ET模式返回读就绪,当buffer中有可写空间(即buffer不满)且用户对相应fd进行epoll_mod OUT事件时返回写就绪。

所以得到如下解决方式:

if(events[i].events&EPOLLIN)//如果收到数据,那么进行读入

{

    cout << "EPOLLIN" << endl;

    sockfd = events[i].data.fd;

    if ( (n = read(sockfd, line, MAXLINE))>0) 

{

line[n] = '/0';

        cout << "read " << line << endl;

if(n==MAXLINE)

{

ev.data.fd=sockfd;

ev.events=EPOLLIN|EPOLLET;

epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //数据还没读完,重新MOD IN事件

}

else

{

ev.data.fd=sockfd;

ev.events=EPOLLIN|EPOLLET;

epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //buffer中的数据已经读取完毕MOD OUT事件

}

}

 else if (n == 0) 

{

close(sockfd);

    }

    

}

else if(events[i].events&EPOLLOUT) // 如果有数据发送

{

    sockfd = events[i].data.fd;

    write(sockfd, line, n);

    ev.data.fd=sockfd; //设置用于读操作的文件描述符

    ev.events=EPOLLIN|EPOLLET; //设置用于注测的读操作事件

    epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);  //修改sockfd上要处理的事件为EPOLIN

}

注:对于write操作,由于sockfd是工作在阻塞模式下的,所以没有必要进行特殊处理,和LT使用一样。

分析:这种方法存在几个问题:

(1) 对于read操作后的判断——if(n==MAXLINE),不能说明这种情况buffer就一定还有没有读完的数据,试想万一buffer中一共就有MAXLINE字节数据呢?这样继续 MOD IN就不再得到通知,而也就没有机会对相应sockfd MOD OUT。

(2) 那么如果服务端用其他方式能够在适当时机对相应的sockfd MOD OUT,是否这种方法就可取呢?我们首先思考一下为什么要用ET模式,因为ET模式能够减少epoll_wait等系统调用,而我们在这里每次read后都要MOD IN,之后又要epoll_wait,势必造成效率降低,这不是适得其反吗?

综上,此方式不应该使用。

l 方法二

读: 只要可读, 就一直读, 直到返回 0, 或者 errno = EAGAIN 

写: 只要可写, 就一直写, 直到数据发送完, 或者 errno = EAGAIN 

  

if (events[i].events & EPOLLIN) 

  {

  n = 0;

      while ((nread = read(fd, buf + n, BUFSIZ-1)) > 0) 

  {

  n += nread;

      }

 if (nread == -1 && errno != EAGAIN) 

  {

 perror("read error");

      }

      ev.data.fd = fd;

      ev.events = events[i].events | EPOLLOUT;

      epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);

  }

  if (events[i].events & EPOLLOUT) 

  { 

  int nwrite, data_size = strlen(buf);

      n = data_size;

      while (n > 0) 

  {

  nwrite = write(fd, buf + data_size - n, n);

          if (nwrite < n) 

  {

             if (nwrite == -1 && errno != EAGAIN) 

 {

 perror("write error");

             }

             break;

           }

          n -= nwrite;

        }

    ev.data.fd=fd; 

    ev.events=EPOLLIN|EPOLLET; 

    epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);  //修改sockfd上要处理的事件为EPOLIN

 }  

注:使用这种方式一定要使每个连接的套接字工作于非阻塞模式,因为读写需要一直读或写直到出错(对于读,当读到的实际字节数小于请求字节数时就可以停止),而如果你的文件描述符如果不是非阻塞的,那这个一直读或一直写势必会在最后一次阻塞。这样就不能在阻塞在epoll_wait上了,造成其他文件描述符的任务饿死。

综上:方法一不适合使用,我们只能使用方法二,所以也就常说“ET需要工作在非阻塞模式”,当然这并不能说明ET不能工作在阻塞模式,而是工作在阻塞模式可能在运行中会出现一些问题。

l 方法三

仔细分析方法二的写操作,我们发现这种方式并不很完美,因为写操作返回EAGAIN就终止写,但是返回EAGAIN只能说名当前buffer已满不可写,并不能保证用户(或服务端)要求写的数据已经写完。那么如何保证对非阻塞的套接字写够请求的字节数才返回呢(阻塞的套接字直到将请求写的字节数写完才返回)?

我们需要封装socket_write()的函数用来处理这种情况,该函数会尽量将数据写完再返回,返回-1表示出错。在socket_write()内部,当写缓冲已满(send()返回-1,且errno为EAGAIN),那么会等待后再重试.

ssize_t socket_write(int sockfd, const char* buffer, size_t buflen)

{

  ssize_t tmp;

  size_t total = buflen;

  const char* p = buffer;

  while(1)

  {

    tmp = write(sockfd, p, total);

    if(tmp < 0)

    {

      // 当send收到信号时,可以继续写,但这里返回-1.

      if(errno == EINTR)

        return -1;

      // 当socket是非阻塞时,如返回此错误,表示写缓冲队列已满,

      // 在这里做延时后再重试.

      if(errno == EAGAIN)

      {

        usleep(1000);

        continue;

      }

      return -1;

    }

    if((size_t)tmp == total)

        return buflen;

     total -= tmp;

     p += tmp;

  }

  return tmp;//返回已写字节数

}

分析:这种方式也存在问题,因为在理论上可能会长时间的阻塞在socket_write()内部(buffer中的数据得不到发送,一直返回EAGAIN),但暂没有更好的办法。

不过看到这种方式时,我在想在socket_write中将sockfd改为阻塞模式应该一样可行,等再次epoll_wait之前再将其改为非阻塞。

5.2 ET模式下的accept

    考虑这种情况:多个连接同时到达,服务器的 TCP 就绪队列瞬间积累多个就绪

连接,由于是边缘触发模式,epoll 只会通知一次,accept 只处理一个连接,导致 TCP 就绪队列中剩下的连接都得不到处理。

     解决办法是用 while 循环抱住 accept 调用,处理完 TCP 就绪队列中的所有连接后再退出循环。如何知道是否处理完就绪队列中的所有连接呢? accept  返回 -1 并且 errno 设置为 EAGAIN 就表示所有连接都处理完。 

的正确使用方式为: 

while ((conn_sock = accept(listenfd,(struct sockaddr *) &remote, (size_t *)&addrlen)) > 0) {   

    handle_client(conn_sock);   

}   

if (conn_sock == -1) {   

     if (errno != EAGAIN && errno != ECONNABORTED    

            && errno != EPROTO && errno != EINTR)    

        perror("accept");   

} 

扩展:服务端使用多路转接技术(select,poll,epoll等)时,accept应工作在非阻塞模式。 

原因:如果accept工作在阻塞模式,考虑这种情况: TCP 连接被客户端夭折,即在服务器调用 accept 之前(此时select等已经返回连接到达读就绪),客户端主动发送 RST 终止连接,导致刚刚建立的连接从就绪队列中移出,如果套接口被设置成阻塞模式,服务器就会一直阻塞在 accept 调用上,直到其他某个客户建立一个新的连接为止。但是在此期间,服务器单纯地阻塞在accept 调用上(实际应该阻塞在select上),就绪队列中的其他描述符都得不到处理。

    解决办法是把监听套接口设置为非阻塞, 当客户在服务器调用 accept 之前中止

某个连接时,accept 调用可以立即返回 -1, 这时源自 Berkeley 的实现会在内核中处理该事件,并不会将该事件通知给 epoll,而其他实现把 errno 设置为 ECONNABORTED 或者 EPROTO 错误,我们应该忽略这两个错误。(具体可参看UNP v1 p363)

转载:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=28541347&id=4308612

epoll ET与LT模式详解 ,但是因为没有输出换行,所以buffer中的内容一直存在,下次epoll_wait的时候,虽然有写空间但是ET模式下不再返回写就绪。2)程序向标准输出输出”welcome to epoll’s world”和换行符,因为标准输出为控制台的时候缓冲是“行缓冲”,所以换行符导致buffer中的内容清空,当有旧数据被发送走时,即buffer中待写的内容变少的时候会触发fd状态的改变,所以下次epoll_wait会返回写就绪,如此循环往复。LT:水平触发,效率会低于ET触发,尤其在大并发,大流量的情况下。 阅读详情

相关推荐

MATLAB实战:四桥臂逆变器3D-SVPWM调制算法仿真全流程(附代码)

本文提供了一份完整的MATLAB/Simulink实战指南,详细解析了四桥臂逆变器的3D-SVPWM调制算法仿真全流程。文章从工程实践出发,手把手指导如何搭建模型,涵盖从abc静止坐标系变换、三维空间矢量区域判定、作用时间计算到PWM信号生成与调试等核心环节,并附有代码示例和常见问题解决方案,旨在帮助工程师快速掌握这一应对不平衡负载的高效调制技术。

qsc90123456的博客 233

操作系统之epoll的LT/ET模式

以前写过两篇文章Linux之五种IO模型以及IO多路复用之select、poll和epoll 梳理了关于IO模型的大体结构框架。 关于本文更详细的解释请看EPOLL的ET和LT模式 一、LT/ET Level Triggered (LT) 水平触发 1、socket接收缓冲区不为空 有数据可读 读事件一直触发 2、socket发送缓冲区不满 可以继续写入数据 写事件一直触发 符合思维习惯,epol...

weixin_42579072的博客 598

Unity3d实现红外热成像效果

1.将需要在红外图像中高亮的物体设置到图层PostProcessing。 2.新建一个相机CameraHighLight,设置其Culling Mask为PostProcessing,也就是在这个相机中只有PostProcessing图层的物体可见。 3.新建一个RenderTexture,命名为HightLightRt,将CameraHighLight的Target Texture设HightLightRt。 4.新建一个相机Camera2,这个相机用于显示其他物体。 接下来将CameraHigh

zouxin_88的专栏 8326

关于epoll的et模式一次没读完,下次有新数据来了,还会触发么?

我是看了群主的公众号以后又疑问,刚好上次头条面试面试官也问我了,然后问大家也是模棱两可,所以我把群主的代码稍微改了下。 公众号名称:高性能服务器开发 问题是:当epoll触发可读时,万一数据没有从缓冲区读完,下次有新的数据过来,还会触发么? 答案是: 会触发 et模式触发是这样说的:由不可读变为可读时触发读事件,由不可写变为可写触发写事件。 有点宏观 然后我们具体看代码: #include<...

大洋深处 3184

USRP B210 运行gnuradio时 FPGA固件版本冲突错误解决方法

事情起源于昨天拿到USRP后开始调试,结果整了一个下午都没解决这个问题。 昨天晚上自己思考了一下,觉得还是版本问题。 首先,uhd_find_devices和uhd_usrp_probe均可以正常运行,没有报错,固件正常加载,序列号也可看到。 teray@teray-X550JK:~$ uhd_find_devices [INFO] [UHD] linux; GNU C++ version 7.5...

斯哈研究所 4242

一文讲透3GPP TS 36.104发射链路相关指标

3GPP是制定移动通信标准的国际组织,其36.104规范对LTE基站射频性能提出详细要求。规范主要涵盖七方面:1)基站输出功率分类标准;2)LTE信道带宽计算方法;3)TD-LTE特有的发射机关断功率及时延要求;4)信号质量指标(频率误差和EVM)及其影响因素;5)邻道泄漏比(ACLR)的45dBc标准;6)带内频谱辐射模板(OBUE)的噪声限制;7)9kHz-12.75GHz频段的带外杂散辐射要求。这些技术要求保障了LTE基站的射频性能和网络质量。

weixin_49761275的博客 4524

linux wait函数头文件_Android 深入底层:Linux事件管理机制 epoll

在linux 没有实现epoll事件驱动机制之前,我们一般选择用select或者poll等IO多路复用的方法来实现并发服务程序。在linux新的内核中,有了一种替换它的机制,就是epoll。select()和poll() IO多路复用模型select的缺点:单个进程能够监视的文件描述符的数量存在最大限制,通常是1024,当然可以更改数量,但由于select采用轮询的方式扫描文件描述符,文件描述符数...

weixin_39633134的博客 374

基于Arduino 的 FM 调频收音机

这是一个基于 Arduino 的开源的收音机项目,它具备一台标准收音机的全部功能,从控制部分到外壳都可以自己 DIY。在项目中,我们将介绍让 FM 收音机调谐器的 RDA5807 模块与 I2C 协议进行通信的方法,整个过程只需要两根线就可以与 IC 通信,接线非常简单。3D 打印的外壳也非常简洁。总之,是个非常简单和实用的项目,来试试吧!

ByteBox的博客 1641

I/O复用之epoll的ET模式

在I/O复用中epoll模式无疑是最为高效的,凭借其底层的红黑树和就绪链表就能省却很多时间,也不用将监视的socket来来回回的从内核拷贝到用户态下,并且他还在更深层次中做了一些我们不知道的事情。但是epoll中ET(Edge Trigger,边沿触发)为我们提供了更高效的解决模式。默认状态下epoll使用的是LT(Level Trigger,水平触发)。      对于LT模式,当epoll_

心之所向,一如既往 558

Mathtype公式下标深度调整技巧:解决固定行距下的显示不全问题

本文针对在Word等文档中设置固定行距时,Mathtype公式下标显示不全的问题,提供了专业解决方案。核心在于调整Mathtype软件内部的“下标深度”参数,通过修改单个公式或批量格式化全文,使公式完美适配固定行距,确保下标清晰完整,且不影响文档整体排版。

pepsi的博客 1497

反应动力学仿真软件:Kintecus_(12).高级功能与技巧

Kintecus允许用户自定义复杂的反应方程,包括非线性反应和多相反应。通过自定义反应方程,用户可以更准确地描述实际反应系统。多尺度仿真是指在同一模型中同时考虑不同时间和空间尺度的反应过程。Kintecus提供了工具来处理从微观分子动力学到宏观反应动力学的多尺度仿真。通过本节的学习,您已经掌握了Kintecus的高级功能与技巧,包括复杂反应网络的构建、参数优化与敏感性分析、自定义反应机制、多尺度仿真、并行计算与批处理,以及数据可视化与后处理。

kkchenjj的博客 1047

彻底学会使用epoll(一)——ET模式实现分析

本文转自:彻底学会使用epoll 1. ET模式实现分析 1.1 ET和LT的实现区别 首先给出下面一张图,这张图是从我之前的一篇博文——epoll实现分析中摘取并细化的。这张图对理解ET模式已经epoll的工作过程只管重要,当然我自己总结出来后也感觉有的小成就,在这里与大家分享。 注:上图的poll不要理解成和select相似那个poll,这是通过epoll_ctl调用的。 下面简要分析一下e...

滴答滴,滴答滴 1040

VCAST创建单元测试工程

选择工作目录,后面创建胡UT工程将会生成到这个目录。

qq_42851449的博客 2479

epoll中的ET和LT模式区别

epoll中的ET和LT模式一、水平触发(LT)和边沿触发(ET)二、例子1.例子:水平触发(LT)2.例子:边沿触发(ET) 一、水平触发(LT)和边沿触发(ET) 在电路中的有水平触发和边沿触发的概念,在epoll读取事件下,水平触发可以理解为,蓝色那一部分,只要存在可读的情况,就会一直读取。而边沿触发,可以理解为红色箭头所指向,发生跳变的部分,就会触发一次。 在epoll中 events=EPOLLIN 为读取事件,LT模式 events=EPOLLIN|EPOLLET 为读取事件,ET模式 e

菊头蝙蝠的博客 5316

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

epoll的ET和LT模式详解

从man手册中,得到ET和LT的具体描述如下EPOLL事件有两种模型:Edge Triggered (ET)Level Triggered (LT)假如有这样一个例子:1. 我们已经把一个用来从管道中读取数据的文件句柄(RFD)添加到epoll描述符2. 这个时候从管道的另一端被写入了2KB的数据3. 调用epoll_wait(2),并且它会返回RFD,说明它已经准备好读取操作4. 然后我们读取了

技术资料库 3万+

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

认识epoll模型的两种工作方式 以及 ET模式必须设为非阻塞的原因

了解EPOLL模型中的ET模式以及使用ET模式的两个要求

challenglistic的博客 3887

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

彻底学会使用epoll(二)——ET的读写操作实例分析

相关视频推荐 面试中正经“八股文”网络原理tcp/udp,网络编程epoll/reactor epoll 原理剖析 以及 reactor 模型应用 epoll原理剖析以及三握四挥的处理 LinuxC++后台服务器开发架构师免费学习地址 彻底学会使用epoll(一)——ET模式实现分析 接上一篇 首先,看程序四的例子。 程序四 #include <unistd.h> #include <iostream> #include <sys/epoll.h> u

资料qun832218493 976
上一篇: 彻底学会使用epoll(四)——ET的写操作实例分析
下一篇: 彻底学会使用epoll(六)——关于ET的若干问题总结
xiaofei0859
博客等级 码龄18年 454粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值