自己趟过epoll的坑

【向量数据库】Milvus向量数据库 ① 从入门到实战 Milvus是专为海量高维向量设计的开源数据库,支持AI搜索、图像识别、推荐系统等场景。相比传统关系型数据库(如PostgreSQL),它更高效处理语义检索、非结构化数据存储,可毫秒级响应亿级向量查询。单机场景下,推荐配置8GB内存/32GB硬盘,支持FAISS/HNSW等索引算法,支持GPU加速。本文涵盖优快云专属部署教程(CentOS脚本/配置调优),提供避坑指南与扩展资源,并对比分析与传统数据库差异。适合算法工程师、AI开发人员快速上手,附官方下载与性能测试技巧,手把手教你搭建高效向量检索系统! 阅读详情

坑的背景

本人用epoll来实现多路复用,epoll触发模式有两种:

  • ET(边缘模式)
  • LT(水平模式)

LT模式

是标准模式,意味着每次epoll_wait()返回后,事件处理后,如果之后还有数据,会不断触发,也就是说,一个套接字上一次完整的数据,epoll_wait()可能会返回多次,直到没有数据为止。

ET模式

也称高效模式,有数据过来后,epoll_wait()会返回一次,一段时间内,该套接字就算有数据源源不断地过来,epoll_wait()也不会返回了。这里注意,是一段时间,不代表这个套接字上有数据就只触发一次。时间过长,还是会返回多次的。比如我写FTP用了epoll+多线程,但是每次套接字上有信息就开线程处理,同一时间内希望一个套接字只被一个线程持有,但是因为文件传输时间过长,就算使用ET模式,套接字还是会返回多次。这里要特别强调一个参数EPOLLONESHOT,如果要保证套接字同一时段只被一个线程处理,必须加上。

解决方案:给accept()后的套接字加上参数EPOLLONESHOT,线程结束后处理完之后,再重置EPOLLONESHOT属性,但是,千万不可以给listen()后的监听套接字设置此属性,这会造成同一时刻只能处理一个连接的情况。

深入理解EPOLLONESHOT事件

即使使用ET模式,一个socket上的某个事件还是可能被触发多次,这是跟数据报的大小有关系,常见的情景就是一个线程,而在数据的处理过程中该socket上又有新数据可读(EPOLLIN再次被触发),此时另外一个线程被唤醒处理这些新的数据,于是出现了两个线程同时操作一个socket,为了避免这种情况,就可以采用epoll的EPOLLONESPOT事件。同时要注意,注册了EPOLLONESHOT事件的socket一旦被某个线程处理完毕,该线程就应该立即重置这个socket的EPOLLONESHOT的事件,以确保这个socket下次可读时,其EPOLLIN事件被触发,进而让其他的工作线程有机会继续处理这个socket。

网络事件EAGIN

在一个非阻塞的socket上调用read/write函数, 返回EAGAIN或者EWOULDBLOCK(注: EAGAIN就是EWOULDBLOCK)从字面上看, 意思是:EAGAIN: 再试一次,EWOULDBLOCK: 如果这是一个阻塞socket, 操作将被block,perror输出: Resource temporarily unavailable.

小结:

这个错误表示资源暂时不够,能read时,读缓冲区没有数据,或者write时,写缓冲区满了。遇到这种情况,如果是阻塞socket,read/write就要阻塞掉。而如果是非阻塞socket,read/write立即返回-1, 同时errno设置为EAGAIN。所以,对于阻塞socket,read/write返回-1代表网络出错了。但对于非阻塞socket,read/write返回-1不一定网络真的出错了。可能是Resource temporarily unavailable。这时你应该再试,直到Resource available。

EAGAIN: 再试一次,EWOULDBLOCK: 如果这是一个阻塞socket, 操作将被block,perror输出: Resource temporarily unavailable。这个错误表示资源暂时不够,能read时,读缓冲区没有数据,或者write时,写缓冲区满了。遇到这种情况,如果是阻塞socket,read/write就要阻塞掉。而如果是非阻塞socket,read/write立即返回-1, 同时errno设置为EAGAIN。所以,对于阻塞socket,read/write返回-1代表网络出错了。但对于非阻塞socket,read/write返回-1不一定网络真的出错了。可能是Resource temporarily unavailable。这时你应该再试,直到Resource available。

综上,对于non-blocking的socket,正确的读写操作为:
读:忽略掉errno = EAGAIN的错误,下次继续读
写:忽略掉errno = EAGAIN的错误,下次继续写

对于select和epoll的LT模式,这种读写方式是没有问题的。但对于epoll的ET模式,这种方式还有漏洞。

epoll的两种模式LT和ET

二者的差异在于level-trigger模式下只要某个socket处于readable/writable状态,无论什么时候进行epoll_wait都会返回该socket;而edge-trigger模式下只有某个socket从unreadable变为readable或从unwritable变为writable时,epoll_wait才会返回该socket。如下两个示意图:
从socket读数据:
socket_read
从socket写数据:
socket_write
所以,在epoll的ET模式下,正确的读写方式为:
读:只要可读,就一直读,直到返回0,或者 errno = EAGAIN 写:只要可写,就一直写,直到数据发送完,或者 errno = EAGAIN。

正确的读:

 
  1. n = 0;
  2. while ((nread = read(fd, buf + n, BUFSIZ-1)) > 0) {
  3. if (nread == -1 && errno != EAGAIN)
  4. {
  5. perror("read error");
  6. }
  7. n += nread;
  8. }

正确的写:

 
  1. int nwrite, data_size = strlen(buf);
  2. n = data_size;
  3. while (n > 0)
  4. {
  5. nwrite = write(fd, buf + data_size - n, n);
  6. if (nwrite < n)
  7. {
  8. if (nwrite == -1 && errno != EAGAIN)
  9. {
  10. perror("write error");
  11. }
  12. break;
  13. }
  14. n -= nwrite;
  15. }

accept上的问题

  • 阻塞模式 accept 存在的问题
    考虑这种情况:TCP连接被客户端夭折,即在服务器调用accept之前,客户端主动发送RST终止连接,导致刚刚建立的连接从就绪队列中移出,如果套接口被设置成阻塞模式,服务器就会一直阻塞在accept调用上,直到其他某个客户建立一个新的连接为止。但是在此期间,服务器单纯地阻塞在accept调用上,就绪队列中的其他描述符都得不到处理。

解决方案:把监听套接口设置为非阻塞,当客户在服务器调用accept之前中止某个连接时,accept调用可以立即返回-1,这时源自Berkeley的实现会在内核中处理该事件,并不会将该事件通知给epoll,而其他实现把errno设置为ECONNABORTED或者EPROTO错误,我们应该忽略这两个错误。

  • ET模式下accept存在的问题。

考虑这种情况:多个连接同时到达,服务器的TCP就绪队列瞬间积累多个就绪连接,由于是边缘触发模式,epoll只会通知一次,accept只处理一个连接,导致TCP就绪队列中剩下的连接都得不到处理。
解决办法是用while循环抱住accept调用,处理完TCP就绪队列中的所有连接后再退出循环。如何知道是否处理完就绪队列中的所有连接呢?accept返回-1并且errno设置为EAGAIN就表示所有连接都处理完。

综合以上两种情况,服务器应该使用非阻塞地accept,accept在ET模式下的正确使用方式为:

 
  1. while ((conn_sock = accept(listenfd,(struct sockaddr *) &remote, (size_t *)&addrlen)) > 0)
  2. {
  3. handle_client(conn_sock);
  4. }
  5. if (conn_sock == -1)
  6. {
  7. if (errno != EAGAIN && errno != ECONNABORTED && errno != EPROTO && errno != EINTR)
  8. perror("accept");
  9. }

一道腾讯后台开发的面试题:

使用Linux epoll模型,水平触发模式;当socket可写时,会不停的触发socket可写的事件,如何处理?
+ 第一种最普遍的方式:
需要向socket写数据的时候才把socket加入epoll,等待可写事件。接受到可写事件后,调用write或者send发送数据。当所有数据都写完后,把socket移出epoll。
这种方式的缺点是,即使发送很少的数据,也要把socket加入epoll,写完后在移出epoll,有一定操作代价。

  • 第二种的方式:
    开始不把socket加入epoll,需要向socket写数据的时候,直接调用write或者send发送数据。如果返回EAGAIN,把socket加入epoll,在epoll的驱动下写数据,全部数据发送完毕后,再移出epoll。
    这种方式的优点是:数据不多的时候可以避免epoll的事件处理,提高效率。

我自己代码的问题

因为我之前才用的是非阻塞ET模式,这样我在发送缓冲数据的数据,会出现EAGAIN的问题。这个问题并不可怕,最可怕的是会发生,因为上面已经有方法解决。为了看起来方便我这边再拷贝一份。但是我源代码采用的writev源代码下载地址614行进行发送,根本无法才采用下面的方法。

 
  1. int nwrite, data_size = strlen(buf);
  2. n = data_size;
  3. while (n > 0)
  4. {
  5. nwrite = write(fd, buf + data_size - n, n);
  6. if (nwrite < n)
  7. {
  8. if (nwrite == -1 && errno != EAGAIN)
  9. {
  10. perror("write error");
  11. }
  12. break;
  13. }
  14. n -= nwrite;
  15. }

我的解决方法采用阻塞写,这个方法很好的解决上面的问题:对于非阻塞的TCP套接字,如果缓冲区根本就没空间,则返回一个EWOULDBLOCK错误。如果缓冲区有一些空间,返回值是内核能够复制到该缓冲区的字节数。这个字节数也叫作不足计数.。详细见我上述连接的代码文件。

在读取缓冲区也是这样。也是采用阻塞进行读取,这样做,虽然降低并发性,但是为了准确处理数据。

总结

ET模式下:
如果read返回0,那么说明已经接受所有数据
如果errno=EAGAIN,说明还有数据未接收,等待下一次通知
如果read返回-1,说明发生错误,停止处理

参考IO几种形式的差别

SAP BTP ABAP RAP 开发对象命名规范与落地实践 摘要:SAP BTP ABAP开发中的命名规范实践 在企业级ABAP开发中,命名规范是提升交付效率的工程化手段。SAP推荐采用[/<namespace>/][<prefix>]_<object_name>_[<suffix>]的结构化命名框架,解决多对象、多团队的协同问题。关键实践包括:使用组织专属命名空间避免冲突;通过prefix区分同类型对象的不同语义(如UI_/API_);object_name体现业务语义而非技术细节;suffix表达次级差异。在具体对 阅读详情

相关推荐

超实用!用ChimeraX对AlphaFold 结果可视化(中): 蛋白结构美化篇

而一般需要的背景颜色是无色(白色),有三种方法可以更改背景颜色,这里我们介绍两种方法,第三种在下一小节中讲解。如果想选中蛋白链中的某一结构区域或者某些位点,可以用Ctrl+Shift+鼠标左键,或在软件窗口中点击。在这个窗口中可以对蛋白的主链、侧链、表面等颜色进行修改,只用在。,即可在右下角看到我们蛋白的序列信息,在这里面直接用鼠标选择要选择的结构域。就会有更多的颜色供我们选择,如果有特定的色号还可以在这个界面继续选择。后面的颜色(②),就可以选择想要的任何背景颜色(③)。就可以选择软件推荐的常用颜色。

m0_63153079的博客 5479

epoll学习总结第一篇

epoll 1. et模式:socket状态改变:缓冲区收到数据、关闭socket、,epoll_wait返回通知一次,不论是否接收完毕仅且通知一次, 例如:客户端发送数据:”hello world!“       服务端epoll_wait返回一次通知,然后recv 5个字节,接收到的是”hello“,此时缓冲区剩余数据为:“ world!”,然后继续epoll_wait,此时不会收到任何

ljq32的专栏 676

AUTOSAR R20-11标准下载!

这是公众号的第2篇推文,粉丝数49 Document Search Classic Platform Adaptive Platform Foundation 文档截图 以为会在明天的发布会之后才上线,没想到官网现在就悄悄放出来了。 中国由于网络问题,下载速度比较慢或者会中断。 “阅读原文”提供多种下载方式,可以在线浏览,可以集中下载也可单个文档下载。 如有增减我也会及时在公众号里通知大家,不过我觉得不会变动了。 整理不宜,好东西要分...

Adaptive_AUTOSAR的博客 1890

死锁的概念,产生和预防

1.死锁的基本概念 死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种 阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了 死锁,这些永远在互相等待的进程称为死锁进程。 2.产生死锁的4个必要条件 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。 请求和保持条件:当进程因请求资源而阻塞时,对已获得...

WsCha0的博客 417

国内开发者福音:手把手教你用微软Authenticator搞定GitHub 2FA验证(附Recovery Codes保存指南)

本文详细介绍了国内开发者如何使用微软Authenticator中国版轻松配置GitHub双重身份验证(2FA),解决短信验证不可用和国际验证器访问困难的问题。重点强调了恢复码(Recovery Codes)的安全备份策略,确保账户安全无忧。

weixin_28840143的博客 435

GAMES101:MacOS作业环境搭建

查看gcc/clang的位置产看gcc/clang的版本1.1.2 安装cmake。

weixin_38844255的博客 715

epoll 事件丢失

如果主accept监听的soctet fd也设置为非阻塞,那么单纯靠epoll事件来驱动的服务器模型会存在问题,并发压力下发现,每次accept只从系统中取得第一个,所以如果恰冯多个 连接同时触发server fd的EPOLLIN事件,在返回的event数组中体现不出来,会出现丢失事件的现象,所以当用ab等工具简单的压载就会发现每次都会有最后几条信息得 不到处理,原因就在于此,我现在的解决

davayunhuijia的专栏 2226

Backblaze 2025全年硬盘故障率报告解读

作为长期追踪存储行业动态的科技博主,每年/季度Backblaze的Drive Stats报告都是我重点关注的核心内容——不同于厂商发布的实验室数据,Backblaze以近13年、数十万片企业级硬盘的真实运行数据为基础,其年化故障率(AFR)、寿命趋势等结论,直接反映了硬盘在严苛数据中心环境下的真实表现,无论是企业存储选型还是个人高端存储参考,都具备极高的实用价值。2025年末,Backblaze发布了第13年的年度收官报告,涵盖2025年Q4、全年及终身硬盘可靠性数据。

存储随笔 4011

TCP连接大量CLOSE_WAIT状态问题排查

CLOSE_WAIT产生原因   close_wait是被动关闭连接是形成的,根据TCP状态机,服务器端收到客户端发送的FIN,TCP协议栈会自动发送ACK,链接进入close_wait状态。但如果服务器端不执行socket的close()操作,状态就不能由close_wait迁移到last_ack,则系统中会存在很多close_wait状态的连接,如下图所示: 可能的原因如下: ...

u010325193的博客 1万+

论文复现《基于最大李雅普诺夫指数的电力系统暂态电压稳定评估方案》

电力系统暂态电压稳定评估方案 摘要 本研究提出了一种基于最大李雅普诺夫指数的电力系统暂态电压稳定评估方法。通过分析相空间中相邻轨迹的收敛或发散特性,该方法能够提前判别系统稳定性。论文首先阐述了暂态电压稳定机理,详细介绍了最大李雅普诺夫指数的计算方法。针对IEEE-39节点系统,建立了仿真模型并获取不同故障场景下的数据。研究开发了完整的评估程序,包括数据预处理、相空间重构、指数计算等模块,并通过与时域仿真结果的对比验证了方法的准确性。实验结果表明,该方法能够快速有效地评估电力系统的暂态电压稳定性,为系统安全运

max500600的博客 186

【C语言学习疑难杂症】第20期:0x00007FF7F2821964处引发的异常0xC0000005写入位置 0x0000000000000

检查代码中是否存在野指针访问的情况,确保在进行写入操作之前,内存已经被正确分配或初始化,并且在使用完毕后进行了正确的释放。空指针访问:代码尝试在空指针地址(0x0000000000000000)处进行写入操作,导致访问冲突。检查代码中是否存在数组越界的情况,确保在进行写入操作之前,数组的索引值在合法范围内。检查代码中是否存在空指针访问的情况,确保在进行写入操作之前,指针已经被正确初始化。野指针访问:代码尝试在已释放或未初始化的内存地址进行写入操作,导致访问冲突。

学无止境 828

epoll ET模式 注意事项

1、server端的fd不需要设置et模式 我们在创建socket成功后会有个listenfd,listenfd = socket(AF_INET, SOCK_STREAM, 0) 然后会把这个fd加入epoll wait队列中,网上很多没有经过验证的代码是这样写的: ev.data.fd = listenfd; ev.events=EPOLLIN|EPOLLE

RandyLeonard的专栏 1542

未连接互联网的银河麒麟v10安装g++,各种报错,如何处理?

🏆本文收录于《全栈Bug调优(实战版)》专栏,主要记录项目实战过程中所遇到的Bug或因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 备注:部分问题/疑难杂症搜集于互联网。

**My Coding Family** 1803

epoll原理分析,图文并茂讲解epoll,彻底弄懂epoll机制

epoll是网络编程中非常难的一点,通过本文学习能彻底了解epoll原理,初学者少走弯路

weixin_28673511的博客 8969

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

visio安装包

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

epoll使用详解:epoll_create、epoll_ctl、epoll_wait、close

epoll - I/O event notification facility 在linux的网络编程中,很长的时间都在使用select来做事件触发。在linux新的内核中,有了一种替换它的机制,就是epoll。 相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率。因为在内核中的select实现中,它是采用轮询来处理的,轮询的fd数目越多,自然耗时越多。并且,在linux/posix_types.h头文件有这样的声明: #define __FD_SETSIZE 1024...

jfcat的专栏 3094

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

epoll详解【epoll_create】【epoll_ctl】【epoll_wait】

文章目录0 - 前言1 - epoll接口epoll_createepoll_ctlepoll_wait 0 - 前言 参考:epoll机制:epoll_create、epoll_ctl、epoll_wait、close 1 - epoll接口 epoll_create #include <sys / epoll.h> nfd = epoll_creat(max_size); 创建一个内核事件表 (也叫epoll句柄),nfd为epoll句柄(不了解句柄看下一段),参数max_size

日月既往,不可复追 2225

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

epoll使用详解 epoll_create epoll_ctl epoll_wait close

文章目录前言一、epoll接口1 int epoll_create(int size)2 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)3 int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout)二、工作模式关于ET、LT两种工作模式ET模式与LT模式的区别在于三 使用epoll四 完整示例 前言 在linux网络编

goosse的博客 2044

Epoll

目录Epoll - IO多路复用怎么理解创建Epoll向EPOLL对象中添加、修改或者删除感兴趣的事件收集在epoll监控的事件中已经发生的事件完整代码 Epoll - IO多路复用 怎么理解 类比快递员寄快递,传统方式是快递员挨家挨户上楼敲门现,在改为用快递柜子的方式,比如丰巢类似,现在这里有两个集合。 1.住户所有人是一个集合。 2.丰巢是一个子集,比如今天要寄快递的住户。 epoll就是管理这两个集合。 epoll_create相当于一栋楼,聘请了一个快递员,创建一栋楼 epoll_ctl往楼里搬住

方池安夏的博客 1832

epoll用到的epoll_create,epoll_ctl, epoll_wait三个函数,以及epoll的工作模式

1、epoll_create函数 函数声明:int epoll_create(int size) 该 函数生成一个epoll专用的文件描述符。它其实是在内核申请一空间,用来存放你想关注的socket fd上是否发生以及发生了什么事件。size就是你在这个epoll fd上能关注的最大socket fd数。 2、epoll_ctl函数 函数声明:int epoll_ctl(in...

qq_23114283的博客 3490

epoll详解及坑

epoll是select、poll 的改进版。 使用select、poll的缺点: (1)调用select 时,需要将用户空间的所有fd集合拷贝进内核空间。 (2)调用select 时,需要在内核空间遍历所有fd的状态。 (3)select 支持的fd 数目有限,不超过1024。 关于e...

tyhaotingdege的博客 5588

epoll_create, epoll_ctl和epoll_wait

名词解释:man epoll之后,得到如下结果:  NAME         epoll - I/O event notification facility  SYNOPSIS         #include   DEscrīptION

3万+

epoll_create, epoll_ctl和epoll_wait 实例讲解

epoll_create, epoll_ctl和epoll_wait 实例讲解 Admin 2012年2月2日名人名言:人必须像天上的星星,永远很清楚地看出一切希望和愿望的火光,在地上永远不熄地燃烧着火光。——高尔基 NAME        epoll - I/O event notification facility SYNOPSIS        #includ

小忒的学习与生活 3万+

epoll

1、epoll epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。epoll除了提供sel...

king_weng的博客 991

epoll事件

在epoll中,主要涉及到3个函数:epoll_create()、epoll_ctl()以及epoll_wait(),详细内容如下:(《节选自Linux高性能服务器编程》) 其中,epoll_event的事件如下: –EPOLLIN,读事件 –EPOLLOUT,写事件 –EPOLLPRI,带外数据,与select的异常事件集合对应 –EPOLL...

ZGT的博客 4902

epoll概述和epoll内核源码分析(epoll最全讲解)

epoll概述 epoll是linux中IO多路复用的一种机制,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。当然linux中IO多路复用不仅仅是epoll,其他多路复用机制还有select、poll,但是接下来介绍epoll的内核实现。 网上关于epoll接口的介绍非常多,这个不是我关注的重点,但是还...

Tasdily的博客 2079

epoll_create, epoll_ctl和epoll_wait 函数用法实例讲解,总结得不错

http://apps.hi.baidu.com/share/detail/31300135 NAME epoll - I/O event notification facility SYNOPSIS #include <sys/epoll.h> DEscrīptION epoll is a variant of poll(2) that c...

wang13342322203的博客 3030
上一篇: epoll中et+多线程模式中很重要的EPOLL_ONESHOT实验
下一篇: Linux Socket 事件触发模型 epoll 示例 这里会写一个用C语言的TCP服务器的完全实现的简单程序
linuxheik
博客等级 码龄15年 865粉丝 135原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值